Oct 28, 2010

Note 2 Self: MSXML 6.0 Parser "¤%¤&W¤"¤"¤"

I keep forgetting this: If an .NET 3.5 SP 1 install fails, and I yet again fail to consult the logs, as I ALWAYS DO, it is because of that nefarious MSXML 6.0 Parser, may it burn in a very hot place!

MSXML 6.0 Parser, if already installed, must be uninstalled before a .NET 3.5 SP1 update can be performed. Otherwise it will block the entire install, and those thrice damned error messages will keep popping up.

Amazing that I have to rediscover this fact time and time again.

Early Alzheimer? Sure don't hope so!

Oct 11, 2010

SharePoint and Enterprise Portal file extensions explained

So, just a quick primer on common file extensions in SharePoint and EP.

  • *.cab = Cabinet File, a setup file from the olden days that can be made using MAKECAB.EXE. It includes a manifest.xml telling what do to and where, and the folder structure within the cab is important when deploying.
  • *.wsp = Windows SharePoint solution. This is essentially a modern day cab file, and IS a renamed cab file. For more info check out: http://msdn.microsoft.com/en-us/library/aa543741.aspx Note that changing the extension back to .cab again will allow you to take a peek inside.
  • *.dwp = Web Part. This is the pre-ASP.NET 2.0 SharePoint web part format. Remember that web parts originated in SharePoint and that the ASP.NET team found the idea so appealing that they brought it into ASP.NET 2.0. In SharePoint 2003 and earlier web parts were a SharePoint specific phenomenon (though they borrow heavily from Lotus portlets and similar, but that is another matter).
  • *.webpart = Web Part. This is the ASP.NET 2.0+ web part format. It is the recommended format of modern day SharePoint web parts.
Note: dwp and webpart files are deployed in... you guessed it: A cabinet file, errr, I mean wsp file.

Oct 4, 2010

The Zen of Portal Administration
Duplicating Enterprise Portal


On the net it is easy to find an article about setting up multiple enterprise portals on the same machine, just consult either of the following links:

http://geekswithblogs.net/Prabhats/archive/2008/08/12/microsoft-dynamics-ax-2009-enterprise-portal-development-again.aspx

http://blogs.msdn.com/b/solutions/archive/2006/09/11/ep-configuration-single-web-server-multiple-aos-installations.aspx

The purpose of this article is to do the same, that is setting up multiple enterprise portals, but I am gonna do it with a twist. I am gonna do it manually, without any of Dynamics Ax installers and setup options.

So, if you want to know how Enterprise Portal is constructed, do read on. If not, well, consult the first of the linked articles (above).

Terms used in this article:

SharePoint Root aka the XX HIVE (50 HIVE = SharePoint Team Services 1.0, 60 HIVE = SharePoint Portal Server 2003, 12 HIVE = Microsoft Office SharePoint Server 2007, 14 HIVE = SharePoint Server 2010) is the file location for the base SharePoint files.

The SharePoint Root for SharePoint 2007 (12 HIVE) is:
C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12

Pre-requisites

This article assumes that you have installed Ax, Installed SharePoint and deployed enterprise portal. So, if you are going to try this out you must have an existing enterprise portal site.

The Four Technologies of Enterprise Portal

To understand the architecture behind Enterprise Portal, and how it is deployed on your servers, you need to understand the Technologies used in EP: MSSQL, IIS, Dynamic Ax (AOS and client), and Standard SharePoint.

Basically an Ax Enterprise Portal consists of Standard SharePoint with some additional elements. Therefore it is logical to start with standard SharePoint. When I mention standard SharePoint elements, understand that Ax EP also requires the precise same elements, plus some more.

A Standard SharePoint Installation

A standard SharePoint installation requires the following components: IIS and MSSQL (and SharePoint, doh).

IIS hosts the SharePoint WFE (Web Front End).

MSSQL hosts the SharePoint configuration data, and the SharePoint content bases.The content base is where a single SharePoint Web Application stores all documents, sites, and so on.

When these components are in place, the SharePoint installation process creates the so called SharePoint Root, or HIVE as it was called in the old days. The SharePoint Root contains the base application files used by IIS and ASP.NET to host the web sites. It also contains files for web services and so on, but that is outside the scope of this article. The installation process also pushes content bases (Initially just the one for Central Admin) and configuration data to MSSQL, and it creates the Central Administration Virtual Root Folder in x:\inetpub\wss\VirtualDirectories\

SharePoint also modifies settings you can access in IIS, like application pools and site bindings and settings. It does this first for Central Administration, then each time you choose to create a new Web Application (A web application is one of those web sites you find under “Sites” in IIS Manager)

Later, after the installation, each time you create a new Web Application SharePoint creates a new content base (unless you specifically choose to attach an old one), and a new inetpub directory to host Web Application specific data. As already mentioned, IIS settings are modified as well (see the article on Configuration Files in this same blog).

Note the registry and the GAC are also tweaked with, but that is irrelevant in this context.

The Enterprise Portal Extras

In addition to the three above mentioned  pieces of software, Enterprise Portal needs... Dynamics Ax. Of course you have Enterprise Portal tools and the business connector, but let us focus on the major players here.


Using Winmerge, a diff tool, I compared the inetpub folders of a standard SharePoint site and an EP site. What I saw was the following:

• Enterprise Portal has added myriad new files to the root virtual directories.

• Enterprise Portal has NOT deleted any of Standard SharePoint’s files.

• The only file that has been modified is web.Config of the inetpub site.

Note that a lot of files have been added to SharePoint Root as well. I will not go through those in this blog entry, for the sake of brevity.

Manually Duplicating an Enterprise Portal Site

1. First you need to enter SharePoint Central Administration, and create a new web application. This copies everything SharePoint needs into a web application. A web application is a web site, and is shown in IIS under the sites node. In my test I used port 1337 to create the EP web site.

  IMPORTANT: Make sure you create a new Application Pool for the EP site and
copy the application pool settings of the existing EP application pool.. An application pool is a construct that came with IIS 6.0. It is a process that hosts one or several sites (web applications) in the context of a given user. It increases security in that critical errors only affect those applications in that pool.

Trivia: For a long time Apache had a hold over IIS because Apache immediately restarts itself If an error occurs, while old IIS 5.0 crashed and burned, taking down everything on the web server with it. When application pools were introduced IIS finally had a mechanism with which to protect the web server. Application pools can be recycled at regular intervals, or they can be recycled manually. Note that recycling the application pool hosting your EP portal is usually better than restarting IIS, which affects all web sites.

2. Delete the virtual root folder created by SharePoint. In my test I found it at: c:\inetpub\wss\Virtual Directories\1337\

3. Copy and paste the old EP virtual folder, usually found at c:\inetpub\wss\Virtual Directories\80\. Rename the copied folder to 1337.

4. As indicated by the linked articles you need to add content to the XML config file web.Config. You also need to link to an .axc file (Axapta configuration).

a. Put this in <configsections>

<sectiongroup name="Microsoft.Dynamics">
<section name="Session" type="System.Configuration.SingleTagSectionHandler, System, Version=1.0.5000.0, Culture=neutral,PublicKeyToken=b77a5c561934e089">
 </sectiongroup>


 So... What does this do? A sectiongroup under ConfigSections is required for ASP.NET to authenticate a web.Config section node. So, with this in place we are permitted to create a section. Without this you would get not be allowed to insert the below XML pointing to an Axapta configuration file.

b. Create a new node under, NOT IN, <system.web>. That is right, on the line AFTER </system.web>

<microsoft.dynamics>
<session configuration="C:\Inetpub\wwwroot\AOS2_USR.axc" timeout="15">
</microsoft.dynamics>




This is the content that makes the difference. This is what makes multiple portals co-exist, so this is important. Note that we point to an axapta configuration file that I have called AOS2_USR.axc. That is just an axc I created. The naming suggests that this particular axc links to an AOS called AOS2, and the usr layer of that AOS. This is just my naming, your axc may be called butterflies.axc for that matter. The important lesson here is: point to an axc starter file and assign a timeout value. 15 is the web tutorial standard, but if your network is particularly bogged down you might want to increase that value. 


5. So… What do we have now? Well, we have a SharePoint installation with the right file system files, but the wrong kind of content base. We now have to make a copy of the existing EP content base, or more exactly, we will backup the existing EP base and restore it into the new Standard SharePoint Content Base. The new SharePoint base has a unique Site ID that it will retain when merged with the old content base. So, now for some STSADM!

a. stsadm.exe -o backup -url http://sitename/sites/sitecollection -filename oldEPbase.dat

b. stsadm.exe -o restore -url http://sitename:1337/sites/sitecollection -filename oldEPbase.dat

 

6. So, we are nearly done! Now you only need to enter Dynamics Ax and Open Administration->Setup->Enterprise Portal->Web Sites and add the port number 1337 to the URL. Apart from the URL, if you followed my tutorial, the port number should be the only difference between the sites. IMPORTANT: Remember to change this only in the AOS pointed to by the .axc file!

 7. If several developers are working on different portals you can enter Windows Control Panel->Administrative Tools and change the user settings for which AOS you use. The settings you change are persisted in the registry under and is used by Visual Studio for Ax Enterprise Portal development as well as Ax Reporting Services development. HKCU\Software\Microsoft\Dynamics\5.0\Configuration


What now? 
Now everything worked perfectly. Or not. We had issues with images in the /layouts/images virtual folder not rendering correctly, but a couple of recycles and refreshes fixed that. SharePoint WFEs DO SHARE the same virtual folders, so this should be a non-issue. However we do have some performance issues on our development server, which might introduce oddities like this.

In my experience SharePoint is at it's best when given enough resources to play with. :)


References: The above linked articles and my colleague Tommy Skaue

Sep 27, 2010

AxWebParts.cab failing to deploy

Today me and my colleague Tommy Skaue encountered an error when updating the Portal using the Ax portal tools under Administration->Setup->EP

We stepped through the code and found that the deployment of the WSP solution* failed with Microsoft.Dynamics.Framework.Portal.dll being denied access  to the GAC.

After trying myriad ways of elevating our rights, we realized that the GAC was being held hostage, meaning a process had locked the entire folder, and thus nothing could be installed there. As it turned out an OS process had locked down the GAC because of critically low system resources.

After freeing up resources we STSADMed the solution back in, then deployed it using Central Administration. This time around there were no errors, so we retried the update command from the EP control panel under administration (we did the same in the first paragraph in this blog entry).

Everything ran smoothly, and we could once again focus on other issues.

* It wasn't really a WSP file, but a cabinet file... AxWebParts.cab. However: WSPs are cabinet files with a different extension, so my logic is sound. :p

Sep 24, 2010

Gearing up for HTML 5

http://creativefan.com/fresh-and-useful-html5-tutorials-techniques-and-tricks/

The SharePointers' Guide to Angry Customer Questions!

This is a list of various features not supported in this and that browser.

Consult this list at once!

http://technet.microsoft.com/en-us/library/cc263526.aspx

Also, remember that you need to enable WebDAV for some of the Office interoperability functions.

If "New Document" doesn't work: The ActiveX isn't loading as it should, you got a browser problem, and should consult the article above.

If "Save Document" (In Word or Excel 2010 or 2007) takes you to the local file system on a Windows 2008 client (terminal servers for instance): WebDAV probably hasn't been configured for the client, and you should consult the article below.

Enabling Desktop Experience for WebDAV on Win2k8

Note:
SharePoint has it's own implementation of WebDAV under the hood, even though it doesn't show in Server Manager. WebDAV is already configured and new document functionality comes out of the box for most installations and clients. It is on Windows 2008 clients that problems arise.

Also, as the linked article suggest, Windows Vista needs to have its Web Folders service started.

Sep 23, 2010

The Developer’s Guide to Configuration Files, Part One

XML Configuration files for ASP.NET and IIS

Welcome to the .Config Jungle!

Every .NET developer knows about basic configuration files; web.Config for web sites, applicationHost.Config for IIS, machine.Config for global settings and app.Config for applications. This blog entry here is an in depth exploration of the different configuration files for IIS and ASP.NET, the way IIS 7.0 uses them, and what they contain.

This is not in any way an exhaustive list of .Config files. Do a search for “*.Config” on your server, and you will get a glimpse of what I am referring to.

Luckily  most are app.Config files for any given application. Still, there are other configuration files out there.

Web.Config

Web.Config is the file that 99.99% of all ASP.NET developers out there relate to. It is the meat and staple of the framework, the place where you fine tune your application and where the CLR looks for information on how to behave. In a later entry in this blog we will perform an autopsy on it, but right now: Let’s explore the hierarchy of web.Config files on the server.

Web.Config can be found on several levels throughout the web server. The primary location for the configuration file of any web application is in the virtual directory of the site itself. So if your site is located at c:\inetpub\sitename the web.Config of your web application will also be located in that folder. Furthermore you can place web.Config files in subdirectories to override behavior for files placed in those folders.

There is also a root web.Config in the same folder as machine.Config, as outlined below. If you want to make machine wide settings, but only for your web applications, this is the place to do it! Note that the site’s and subsites’ web.Config settings will override those of the machine level web.Config (and those of machine.Config)

Machine.Config

Base path: %systemroot%\Microsoft.NET\Framework\versionNumber\CONFIG\Machine.config

Examples for framework version 2 and 4:

.NET 2.0: %systemroot%\Microsoft.NET\Framework\v2.0.50727\CONFIG

.NET 4.0: %systemroot%\Microsoft.NET\Framework\v4.0.30319\Config

At this location you find the root configuration file for all web.Config and app.Config files on the machine. Changes made to this file will affect all CLR reliant applications and web applications on the server (using the targeted version of the framework, for instance 2.0 or 4.0), unless local configuration files override that particular section you modified.

When you create a new web.Config file, it copies settings from machine.Config. In a way, machine.Config can be considered a "super class" (java lingo) or "base class" of config files. ^^ 

An example:

A web application targeting .NET 2.0 will use machine.Config settings if, and only if, there are no web.Config rules in the root directory of the web app (i.e. c:\inetpub\myWebSite\web.Config) or in an appropriate subdirectory. So, given the ASP.NET page myConfigUseTest.aspx, located at c:\inetpub\myWebSite\someSubDirectory\ it will first use web.Config settings from c:\inetpub\myWebSite\someSubDirectory\ (if any web.Config file exists there), then from c:\inetpub\myWebSite\ and then, if the root directory web.Config does not provide information, it will look in web.Config and machine.Config, in that order, at %systemroot%\Microsoft.NET\Framework\v2.0.50727\CONFIG.

ApplicationHost.Config

ApplicationHost.Config is IIS, Internet Information Server’s, very own configuration file. When you work in IIS manager, clicking all those fancy buttons, the purely IIS specific info will wind up in ApplicationHost.Config. This means that IIS Manager is nothing but an application using .NET framework’s own configuration management classes to present and change information within web.Config, machine.Config and applicationHost.Config files.

ApplicationHost.Config can be found at %systemroot%\System32\inetsrv\config, which is IIS own folder location on the server. Note that inetmgr.exe is also there, the very executable that is IIS Manager itself.
So, by using IIS Manager the buttons and settings under IIS will make it’s way into applicationHost.Config, but the story does not end there.

Changing any of the ASP.NET settings will change web.Config for the site you have currently selected. That’s right, IIS manager knows which site you are on and changes the appropriate web.Config based on that.
If you are positioned on the IIS server site node itself, those settings will be persisted in the root level web.Config found in the same folder as machine.Config.

Changes to application pools and site bindings will be persisted in applicationHost.Config.


New in IIS 7.0 is the integrated pipeline. In older versions of IIS, the so called classic pipeline was used. It loaded an execution pipeline for IIS, then one for ASP.NET. Problem with this model is that some things, like authentication, happened twice. Once for IIS, then again for ASP.NET. The new integrated pipeline runs everything in one pipeline, which means that authentication and such things happens only once. All of this naturally means that the ISAPI filter that hosted ASP.NET is gone, bye bye aspnet_isapi.dll. ASP.NET is now a first class citizen of the pipeline, and ASP.NET modules can be called before, or after, or in between, IIS modules when the pipeline initially loads. This also means that since ASP.NET is integrated into the pipeline, it’s features can be used on the non-ASP.NET elements in the pipeline as well. For example: You can use ASP.NET authentication to protect older IIS resources in the pipeline, like your company’s legacy ASP pages.

Extending IIS has never been easier. Those horrid ISAPI filters of the past are gone, and in their place are two brand new APIs for injecting company specific behavior into the integrated pipeline.

The first API is a native code C++ API, the second is a managed code .NET API.

Want to change the modules loaded in the integrated pipeline? 

Well, you do that in web.Config, silly!  

So, to the SharePointers out there: web.Config is what loads all those SharePoint specific dll's, and makes SharePoint available to some web applications but not others. 

For the purpose of demonstration I will lastly show the contents of an applicationHost.Config file.

I will not show the whole shebang, but rather a site binding section for a SharePoint portal located under <System.ApplicationHost> and <Sites> 


<site name="SharePoint - 80" id="267757848" serverAutoStart="true">
<application
path="/" applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000">
<
virtualDirectory path="/" physicalPath="C:\inetpub\wwwroot\wss\VirtualDirectories\80" />
<virtualDirectory path="
/_vti_bin" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\isapi" />
<virtualDirectory path="
/_layouts" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts" />
<virtualDirectory path="
/_wpresources" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\wpresources" />
<virtualDirectory path="
/_controltemplates" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\controltemplates" />
</application>
<application path="
/_layouts/images" applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000">
<virtualDirectory path="/" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\images" />
</application>
<application path="
/_layouts/inc" applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000">
<virtualDirectory path="/" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts\inc" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":80:" />
</bindings>
<logFile logFormat="W3C" />
<limits connectionTimeout="00:04:00" />
</site>



 Note the following:
  • The configuration file binds a specific application pool to a site
  • This is where the SharePoint web site is mapped to it's SharePoint Root (12 HIVE) folders
  • If you want a custom virtual directory to place resources in (other than the customary \templates\images etc folders, you could link one up in applicationHost.Config (goes for everyone, not only SharePointers)
  • this is one of the myriad places in which timeout settings can be found

  Trivia:

  • Did you know that the /_vti_bin folder name comes from "Vermeer Technologies Incorporated binary folder". Vermeer was v1.0 of FrontPage Extension, the name it had in the pre-Microsoft days, and as we all know, FP Extensions later became SharePoint Designer. This exiting folder has, among other things, native code DLLs that you can use to rape, errr, communicate with SharePoint (These DLLs bypass all security and force changes upon ONET.XML and other SharePoint files, sort of in a backdoor manner). I am planning a later article called "Integrating With SharePoint", in which I will give code examples on several ways to integrate with SharePoint, and the _vti_bin folder dll's will most certainly be covered!

References:
For the blog entries on configuration files I use the following literature:

Sep 14, 2010

Opening PDFs in SharePoint 2010

So, Microsoft made a change to how SharePoint opens PDFs in SharePoint 2010. Now you gotta enable click and open functionality as an admin, or the user will have to download it and then open it from his workstation.


Apparently this is a feature to prevent buggy applications, like Microsoft deems Adobe Acrobat Reader to be, from running hostile code embedded in the PDF. By default SharePoint sends a new HTTP header called X-Download-Options and sets it to noopen. IE 8 interprets this as "must save to HD before opening". Most normal users, and especially those within intranets, don't care much about the rotten tomatoes that Microsoft, Apple and Adobe throw at each other (they call each other's applications leaky and buggy all the time). We just want to open our PDFs in our browser, and nevermind their security innuendo.

Disabling this "feature" is simple. Head into Central Admin, go to the Manage Web Applications section and highlight the Web App you wanna open PDFs in (probably all of them, one by one). Then click General Settings and change "Browser File Handling" from Strict to Permissive.

If you wanna test your settings, you can run this Powershell script (you probably want to change the URLs)

$site = Get-SPSite(“http://intranet")
$web = $site.OpenWeb("/market")
$list = $web.GetList("http://intranet/market/PDFs")
Write-Host $list.browserfilehandling


 
If your settings were applied correctly, your list should be marked as Permissive, which will be echoed into the Powershell command line.

Sep 6, 2010

Problem with SharePoint 2007 upgrade to 2010 - "One or More FieldTypes Are Not Installed Properly"

Recently I came across a problem when upgrading from SharePoint 2007 to SharePoint 2010.


I got a cryptic error telling me that one or more of SharePoint's field types had been installed incorrectly.

Looking at the Publishing Site "Pages" document library was in vain, none of the columns offered any obvious answer. After much toil and labor I found out that http://sitepath/Relationships%20List/allitems.aspx was to blame. The GroupID site column component, which was formerly a GroupGUID (in 2007), was a GUID no longer. So I entered site settings, switched off the Publishing Infrastructure site service, deleted the Relationships list, reenabled the Publishing Infrastructure and voila, everything worked.

Thanks to the peeps at:
http://www.go4answers.com/Example/error-starting-sharepoint-2010-beta-2-44876.aspx

Aug 11, 2010

The cryptic AotNodeNotFoundException unearthed

Today I encountered a strange error, the AotNodeNotFoundException. Well, it doesn't sound strange, but the Aot Node did in fact exist, and everything looked to be in perfect order.

Checking more closely I noticed that the AotNodeNotFoundException referenced a slightly shorter AOT node name than Visual Studio and C# did. It turned out there was an AOT node max length limit, and names longer than that were truncated. That left a reference to a truncated user control.

When I shortened the name of the user control, and updated the AOT references accordingly, everything worked just fine.

So keep those AOT node names trimmed!

Jun 25, 2010

The Zen of Creation
The Art of Creating New Records in Enterprise Portal

In Microsoft Dynamics Ax creating a new record is usually one of the easiest things you could possibly do. When in a grid, press ctrl+n, or the document icon, and there you go: a new record for you complete with required fields underlined in red. Press ctrl+s, or the floppy disk icon, and the records have been saved for posterity.

In Dynamics Ax you get everything free. You might not realize it, but the system synchronize your lookups based on which other field values you might have set, leaving the developer with the comfortable task of focusing on other issues than record creation.

Not so in Enterprise Portal 2009!

Pressing Ctrl+N in Enterprise Portal invokes browser behavior, usually a new browser page, which is not at all what you want! Record creation has to be coded from A to Z by the portal developer! 

This blog entry here aims to disclose the methods I have used over the years to do so along with the pros and the cons.

Method number 1: The ASP.NET wizard tag
Microsoft pushed this method onto the Enterprise Portal developers back when the product shipped. Let me just warn you: This approach is horrible. ASP.NET wizards have a bad reputation among .NET developers, “you didn’t use the ASP.NET framework wizard tag, did you?” being a common comment from a seasoned programmer. Looking at Microsoft’s own code it becomes evident that the wizard is substandard, for all record creation within the product happens using other methods.
I quickly abandoned this approach, after banging my head against the wall for several days. The approach was uncompromising, hard to implement and left the developer with little control over the flow of execution.

Method number 2: ASP.NET code behind insertion

It is pretty easy to insert code in the code behind given the following format:

DataSetView dsv = ds.GetDataSet().DataSetViews[0];
if(dsv != null)
{
DataSetViewRow dsvr = dsv.AddNew();
      Dsvr.BeginEdit();
      Dsvr.SetFieldValue(“TableField”, TxtBoxFromAspPage.Text);
      Dsvr.EndEdit();
}

Barring conversion issues this is all fine and dandy, but the method has some severe shortcomings.

1. As already mentioned (Dynamics Ax) X++ to .NET data type conversion is truly painful.

2. Field dependencies do not update automatically, meaning that if field B is dependent on field A, the asynchronous nature of Enterprise Portal means the developer has to synchronise manually.

3. Catch 22 type scenarios caused by the asynchronous nature of the web and the strong validation performed by .NET: These problems can be illustrated by the following example: You are creating a “Create New Wizard” for a software vendor specific ERP web module. One of the fields, the sales ID lookup, is dependent on the project ID. Now, to begin with there is no project ID, or there is one but you changed it. How can we filter the lookup now when you need to do a postback to register the project lookup change? Furthermore the sales ID is a required field, but without a predefined project ID your lookup returns empty! This means that you cannot save the record, for there is no sales ID, but you cannot get a sales ID for the project Id has not been saved. There you go, catch 22!

The ease of use combined with datatype conversion issues of this method means I primarily use it for simplistic tasks with no advanced filtering or field interdependencies.

Method number 3: DataSet method insertion

In the Dynamics Ax dataset you can define methods that can be called from your C# code. Doing so is fairly straightforward with the first parameter being the method name and the next parameters being the method parameters, up to a max of 3 (4 with the method name). Don’t panic yet, you can send a parameter array as the first parameter, making sure you can send as much as you need.

In C# the code looks like this:

Object[] paramList = new Object[2];
paramList[0] = TxtProjectId.Text;
paramList[1] = TxtSalesId.Text;

try
{
ProjTableDS.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("CreateNew", paramList);
}
catch (System.Exception exception)
{
AxExceptionCategory exceptionCategory;

if (!AxControlExceptionHandler.TryHandleException(this, exception, out exceptionCategory))
{
throw;
}
}

And in X++ the dataset method code looks like this:

public void createNew(str _projId, str _salesId)
{
    SalesId salesId = _salesId; //I will explain this below
    ProjId projId = _projId;
    ;
    info(“ProjId = “ + _projId); //Not much action here
    info(“SalesId = “ + _salesId); //just to illustrate
}

This might seem a lot more complicated than the method number 2, but in my opinion it is the best, most flexible way of doing things.

Note that I pass all parameters as strings. There is a reason for me doing so: datatype conversion is a true pain between X++ and .NET. You do have a vast number of Proxy helper classes that you can pass, but knowing Microsoft and the business I can tell you don’t have any guarantee that those data type mappings will stick in the future, especially between different versions of Ax and .NET.

Strings on the other hand will, and passing strings from .NET and having X++ do the conversion you are certain you’ve got a winning formula. Application internal data type conversion will always be stable, and not prone to MS data type mapping schizophrenia.

I have built SharePoint based Ax EP applications several years now and this method has proven to be the easiest to implement, quickest, most stable and truly a winning formula.

Method number 4: Proxy class insertion

Enterprise Portal Proxy classes is perhaps the #1 source of confusion for Ax techs and developers. In my experience they are cumbersome, difficult to edit, difficult to deploy and generally annoying.

They are also very powerful.

You can expose anything to .NET using the proxy methodology. In the AOT, lookup Web->Web Files->Static Files->Proxies and you find the file you need to register your proxy class, table, enum or what-have-you in. After having registered your object there you can invoke it in .NET.

Behind the scenes this creates a C# .cs file that you must deploy to the virtual folder of the SharePoint site. These sites can be found under X:\Inetpub\wss on the server and is usually called the same as the port number the site is hosted on, typically 80. Under the 80 folder look for app_code\proxies which is the subfolder in which the .cs file must be placed. This file provides wrapper classes used to contact the .NET Businesss Connector and ultimately Dynamics Ax.

If your records require advanced functionality it might be an idea to register the class that creates them as a proxy class, then call it straight from C#.

This is how you might call such a proxy class:

using Proxy = Microsoft.Dynamics.Portal.Application.Proxy;
Proxy.DemoProxyClass proxy = Proxy.DemoProxyClass(AxSession.AxaptaAdapter);
proxy.insert(parameter1, parameter2, …, parameterX);
proxy.runImportantJob();

Invoking it from C# is kinda straight forward, but there are some issues:
1.       Administrators will be dumbfounded by your need to have files under the SharePoint site virtual folder (IIS virtual folder).
2.       
      You will be dumbfounded by how hard it is to update the proxy code. Imagine rewriting the X++ proxy code, recreating the .cs file, restarting the AOS, updating the AOD and still the old code is executed. That is what you are looking at.
Proxies are powerful however, and enable you to tightly integrate your portal with Dynamics Ax, but remember to handle with care and do not use them lightly.

Method number 5: FormDataSource insertion

This method is a variant of method number 2 really.

FormObjectSet formDataSource;
formDataSource = SalesTable_DS.GetDataSet().DataSetRun.dataSource();
formDataSource.create();
formDataSource.initValue();

The main difference between this method and method number 2 is that the strong .NET validation is not invoked as the creation happened serverside. This can be a boon in catch 22 situations, as described under method number 2 (above).

This code contacts the dataset and invokes create on the datasource. You could add custom code to the create method (just override it if it doesn’t exist), enabling you to do a lot of easy work in X++ almost transparently.

As with the other means of contacting the dataset, this methodology is battle tested, works and is a good option.

Method number 6: .NET Business Connector insertion

You can go beyond the wrapper classes and invoke the .NET BC directly, though I cannot imagine why you would do so. I mention it anyhow, for in cases where you do not employ Enterprise Portal, but still want to contact Dynamics Ax (for instance from an ASP.NET web application or standard SharePoint site) it is the only way to go.

Axapta ax = new Axapta();
String company = “DMO”;
String AOSInstance = “02”;
String AOSServerName = “THINK”;
String AOSPort = “49999”;
AxaptaRecord axRec;
ax.Logon(company, "", AOSInstance + "@" + AOSServerName + ":" + AOSPort, ""); //log on to Axapta
axRec = ax.CreateAxaptaRecord(“SalesLine”);
axRec.set_Field(“PurchQty”, “1000”);


In the end business connector calls like these are what it all comes down to. The above code should be wrapped in try catches the way babies should be wrapped in their mommas’ arms, but I removed redundant code to emphasize on what’s important.

Jun 22, 2010

Filtering Enterprise Portal, the difference between UserFilter and standard ranges

Today I discovered a subtle, but important, difference between the two of the filtering methods we can use in Enterprise Portal 2009.

Given the following two sections of code in the Page_Load method of the ASP.NET code behind, and given the following:

String TID = this.Page.Request.QueryString.Get("TID");
if(!String.IsNullOrEmpty(TID))
{
//Method 1 or Method 2 here.
}

Method 1: UserFilter

DataSetView dsv = this.AxDataSource1.GetDataSet().DataSetViews[0];
dsv.UserFilter.ClearOpenFilter();
filterObject flt = new filterObject();

flt.name = "TIDFilter";

conditionType cndId = new conditionType();
cndId.@operator = operatorType.eq;
cndId.status = conditionStatus.open;
cndId.attribute = "TrvId";
cndId.value = TID;

flt.conditionCollection.Add(cndId); //add to filter
  dsv.UserFilter.Add(flt); 



Method 2: Standard Ax QueryBuildRange filter

DataSetView dsv = this.AxDataSource1.GetDataSet().DataSetViews[0];

Proxy.Query query = dsv.MasterDataSource.query();
Proxy.QueryBuildRange qbr = query.dataSourceNo(1).findRange(TableDataFieldMetadata.FieldNum(this.AxSession, "AdTrvTable", "TrvId"));
if (qbr == null)
qbr = query.dataSourceNo(1).addRange(TableDataFieldMetadata.FieldNum(this.AxSession, "AdTrvTable", "TrvId"));
qbr.value = TID;

Both of these methods do what you want: They both apply a filter to your Web Grid and displays the data you are interested in.

BUT, and this is an important but, the standard Ax QueryBuildRange limits the datasource to only the records you are interested in. The UserFilter on the other hand merely hides the remaining records, to be available for AJAX lookups and such should the need arise.

If you are dependent on only having the appropriate records in your datasource you should use QueryBuildRange.

If you are displaying different subsets of the datasource at different times you should use a UserFilter to reduce the number of server queries.

May 28, 2010

A Note Concerning Web Reports through Ax and SSRS

I had a chat with some colleagues yesterday and ventured into the "new" reporting framework, SSRS, that will gradually replace standard Ax reporting.

Last year I mass produced web reports for portal customers. Being Microsoft product users they experienced problems with Internet Explorer. Internet Explorer "helps you" by removing CSS formatting when the document prints. For some of the reports that resulted in serious issues, as the alternating line color was removed from the formatting, resulting in reports that were, to put it mildly, hard to read.

To enable CSS background color printing, which at least the Ax Web reports use:

Open Internet Explorer -> Options -> Advanced -> Printer -> Enable printing of background colors and images.

 (or something, I have the Norwegian language version and am not 100% sure of what the english version says, but it should be a close match).

My colleagues argued that chiefly the list reports would be targeted for SSRS, and no one would print those. My experiences last year suggest otherwise.

Believe me, the customers want to be able to print any report, and they want to be able to read it easily as well.

The morale of the story?

Enable that CSS formatting.

May 6, 2010

http://daxguy.blogspot.com/2006/12/temporary-tables.html

Useful walkthrough of temporary tables. I am debugging Ax's Tax Engine to unearth which parameters cause a currency unaware flat VAT of 93 ^^. Let me just say that in the Tax classes your TmpTable-Fu comes in handy. ;)

May 5, 2010

Offer reports as downloadable PDF documents in your Dynamics Ax 2009 Enterprise Portal

One thing I've been tasked with a lot in Ax Enterprise Portal is exposing Dynamics Ax reports through the web  interface. True enough, Microsoft has provided us with the means to expose Ax reports in HTML format, but some serious tweaking is in order to present these (the report query window prompt is, among other things, mysteriously absent in enterprise portal). And when you finally master presenting reports through enterprise portal the client should be disappointed at how it looks. They always are. They have to adjust settings in Internet Explorer to keep the CSS in their printed documents, tweak the CSS and the x++ html generator to make it presentable, and the list goes on.

By now one thing should be clear: Microsoft wants us all to switch to SSRS reporting.

But in my opinion standard Ax reporting is useful and exposing it shouldn't be so hard. Invoices for instance: Customers should really be able to download them from your site, so I have crafted a sample that writes a report to PDF and then offers users to download it through a text and image web link.

I've done this for EP 3.0 as well, and it is actually a lot easier, but the basics remain the same. At the core is the DataSet method X++ code that generates the report.

First, somewhere in your .NET user control*, preferably as the invoice details page loads (The Page_Load method for instance), call your dataset method.


*  In a webdefined control in Ax 3.0 Enterprise Portal (override the layout method instead).

private string InvoiceId;
private string pdfUrl = "about:blank";
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
  InvoiceId = Page.Request.QueryString.Get("IID"); // I passed the invoice id from the grid as a GET parameter
if(!String.IsNullOrEmpty(InvoiceId))
{
  pdfUrl = "http://192.168.88.5:8080/Invoice_" + InvoiceId + ".PDF"; //obviously hard coding isn't the way to go
[...]
try
{
  CustInvoiceJour_DS.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("CreatePDF", InvoiceId); //This is the call to our DataSet method that initiates the X++ code
  InvoiceLink.NavigateUrl = pdfUrl; // object.
}
catch (Exception)
{


throw;
}
}

Here is the X++ DataSet method that generates the PDF invoice.
NOTE: Remember to host the folder as a site in IIS, or use one of Enterprise Portal's public folders. Also, restrict browsing folder contents and remember to delete sensitive customer data after a short while.
NOTE 2: In Ax 3.0 this code must be in the Web Forms run method.


public str CreatePDF(InvoiceId _invoiceId)
{
Args parameters;
ReportRun reportRun;
//Declare SalesFormLetter and PrintJobSettings objects
SalesFormLetter salesFormLetter;
PrintJobSettings printJobSettings;
 CustInvoiceJour cij;
;

SELECT firstonly cij
 where cij.invoiceId == _invoiceId;

parameters = new Args(ReportStr(SalesInvoice));
printJobSettings = new PrintJobSettings();
printJobSettings.setTarget(PrintMedium::File);
printJobSettings.preferredTarget(PrintMedium::File);
 printJobSettings.preferredMailFormat(PrintFormat::PDF); //Not necessary, but residue from the days this method sent an email instead.
printJobSettings.format(PrintFormat::PDF);
 printJobSettings.fileName(@"\\THINK\pdfhost\Invoice_" + cij.invoiceId + ".PDF"); //Write to the IIS hosted virtual directory

salesFormLetter = new SalesFormLetter_Invoice(false);
 salesFormLetter.updatePrinterSettingsFormLetter(printJobSettings.packPrintJobSettings());

parameters.caller(salesFormletter);
 parameters.parmEnum(PrintCopyOriginal::OriginalPrint); //I had to use PrintCopyOriginal::OriginalPrint, instead of ::Original, as other bloggers recommend. Failing to do so meant, in 2009, that the PDF wasn't saved to the file system. In 3.0 ::Original worked just fine.
parameters.parmEnumType(enumnum(PrintCopyOriginal));
parameters.record(cij);
reportRun = new ReportRun(parameters);
reportRun.init();
reportRun.run();

return "";

} 

Hope this helps someone out there!

Thanks to Brandon George and other bloggers out there.
http://dynamics-ax.blogspot.com/2006_01_01_archive.html

Also: Thank you to Justin Biggs
 https://community.dynamics.com/forums/t/32390.aspx