tag:blogger.com,1999:blog-80728248828604883002024-03-13T19:24:47.798+01:00The Code WorkshopThe blog of a .NET developer.The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-8072824882860488300.post-70708936035930218172010-10-28T09:33:00.000+02:002010-10-28T09:33:17.021+02:00Note 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!<br />
<br />
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.<br />
<br />
Amazing that I have to rediscover this fact time and time again.<br />
<br />
Early Alzheimer? Sure don't hope so!The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com1tag:blogger.com,1999:blog-8072824882860488300.post-57019737696939336542010-10-11T11:19:00.001+02:002010-10-11T11:20:51.112+02:00SharePoint and Enterprise Portal file extensions explainedSo, just a quick primer on common file extensions in SharePoint and EP.<br />
<br />
<ul><li>*.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.</li>
<li>*.wsp = Windows SharePoint solution. This is essentially a modern day cab file, and IS a renamed cab file. For more info check out: <a href="http://msdn.microsoft.com/en-us/library/aa543741.aspx">http://msdn.microsoft.com/en-us/library/aa543741.aspx</a> Note that changing the extension back to .cab again will allow you to take a peek inside.</li>
<li>*.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).</li>
<li>*.webpart = Web Part. This is the ASP.NET 2.0+ web part format. It is the recommended format of modern day SharePoint web parts.</li>
</ul>Note: dwp and webpart files are deployed in... you guessed it: A cabinet file, errr, I mean wsp file.The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-33243039738962668442010-10-04T22:54:00.029+02:002012-10-04T20:09:13.503+02:00<span style="font-size: x-large;"><span style="color: #9fc5e8;">The Zen of Portal Administration<br />
</span></span><span style="font-size: x-large;"><span style="color: #9fc5e8;"><span style="font-size: small;"><i>Duplicating Enterprise Portal</i></span></span></span><br />
<br />
<br />
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:<br />
<br />
<a href="http://geekswithblogs.net/Prabhats/archive/2008/08/12/microsoft-dynamics-ax-2009-enterprise-portal-development-again.aspx">http://geekswithblogs.net/Prabhats/archive/2008/08/12/microsoft-dynamics-ax-2009-enterprise-portal-development-again.aspx</a> <br />
<br />
<a href="http://blogs.msdn.com/b/solutions/archive/2006/09/11/ep-configuration-single-web-server-multiple-aos-installations.aspx">http://blogs.msdn.com/b/solutions/archive/2006/09/11/ep-configuration-single-web-server-multiple-aos-installations.aspx</a> <br />
<br />
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. <br />
<br />
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).<br />
<br />
<b>Terms used in this article:</b><br />
<br />
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.<br />
<br />
The SharePoint Root for SharePoint 2007 (12 HIVE) is:<br />
C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12<br />
<br />
<b>Pre-requisites</b><br />
<br />
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.<br />
<br />
<span style="font-size: large;"><span style="color: #9fc5e8;">The Four Technologies of Enterprise Portal</span></span> <br />
<br />
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.<br />
<br />
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.<br />
<br />
<b>A Standard SharePoint Installation</b><br />
<br />
A standard SharePoint installation requires the following components: IIS and MSSQL (and SharePoint, doh).<br />
<br />
IIS hosts the SharePoint WFE (Web Front End).<br />
<br />
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.<br />
<br />
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\<port number=""><br />
<br />
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)<br />
<br />
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).<br />
<br />
Note the registry and the GAC are also tweaked with, but that is irrelevant in this context.<br />
<br />
<b>The Enterprise Portal Extras</b><br />
<br />
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.</port><br />
<br />
<port number="">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:<br />
<br />
• Enterprise Portal has added myriad new files to the root virtual directories. <br />
<br />
• Enterprise Portal has NOT deleted any of Standard SharePoint’s files.<br />
<br />
• The only file that has been modified is web.Config of the inetpub site.<br />
<br />
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.<br />
<br />
</port><port number=""><span style="font-size: large;"><span style="color: #9fc5e8;">Manually Duplicating an Enterprise Portal Site</span></span></port><port number=""> <br />
<br />
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. <br />
<br />
IMPORTANT: Make sure you create a new Application Pool for the EP site and </port><port number="">copy the application pool settings of the existing EP application pool.</port><port number="">. 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.<br />
<br />
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.<br />
<br />
2. Delete the virtual root folder created by SharePoint. In my test I found it at: c:\inetpub\wss\Virtual Directories\1337\<br />
<br />
3. Copy and paste the old EP virtual folder, usually found at c:\inetpub\wss\Virtual Directories\80\. Rename the copied folder to 1337.<br />
<br />
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).<br />
<br />
a. Put this in <configsections><br />
<br />
</port><span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: small;"><port number=""><sectiongroup name="Microsoft.Dynamics"><br />
<section name="Session" type="System.Configuration.SingleTagSectionHandler, System, Version=1.0.5000.0, Culture=neutral,PublicKeyToken=b77a5c561934e089"><br />
</sectiongroup></port></span></span><port number=""><br />
<br /> 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 <microsoft .dynamics="">section. Without this you would get not be allowed to insert the below XML pointing to an Axapta configuration file. <br />
<br />
b. Create a new node under, NOT IN, <system.web>. That is right, on the line AFTER </system.web><br />
<br />
</microsoft></port><span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: small;"><port number=""><microsoft .dynamics=""><microsoft.dynamics> <br />
<session configuration="C:\Inetpub\wwwroot\AOS2_USR.axc" timeout="15"><br />
</microsoft.dynamics></microsoft></port></span></span><span style="font-size: small;"><port number=""><microsoft .dynamics=""><br />
</microsoft></port></span><port number=""><microsoft .dynamics=""> </microsoft></port><port number=""><microsoft .dynamics=""> </microsoft></port><br />
<port number=""><microsoft .dynamics=""></microsoft></port><br />
<port number=""><microsoft .dynamics=""><br />
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. </microsoft></port><br />
<br />
<port number=""><microsoft .dynamics="">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!<br />
<br />
</microsoft></port><span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: small;"><port number=""><microsoft .dynamics="">a. stsadm.exe -o backup -url http://sitename/sites/sitecollection -filename oldEPbase.dat<br />
<br />
b. stsadm.exe -o restore -url http://sitename<span style="color: magenta;">:1337</span>/sites/sitecollection -filename oldEPbase.dat</microsoft></port></span></span><port number=""><microsoft .dynamics=""><br />
</microsoft></port><port number=""><microsoft .dynamics=""><br />
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!<br />
<br />
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</microsoft></port><br />
<br />
<port number=""><microsoft .dynamics=""><b>What now? </b></microsoft></port><br />
<port number=""><microsoft .dynamics=""> <span style="font-family: "Verdana", sans-serif; font-size: x-small;">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.</span></microsoft></port><br />
<br />
<port number=""><microsoft .dynamics=""><span style="font-family: "Verdana", sans-serif; font-size: x-small;">In my experience SharePoint is at it's best when given enough resources to play with. :)</span></microsoft></port><br />
<br />
<br />
<port number=""><microsoft .dynamics=""><span style="font-family: "Verdana", sans-serif; font-size: x-small;">References: The above linked articles and my colleague Tommy Skaue</span></microsoft></port>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com2tag:blogger.com,1999:blog-8072824882860488300.post-72046534874042686832010-09-27T12:20:00.008+02:002010-09-27T12:34:17.389+02:00AxWebParts.cab failing to deployToday me and my colleague <a href="http://blog.skaue.com/">Tommy Skaue</a> encountered an error when updating the Portal using the Ax portal tools under Administration->Setup->EP<br />
<br />
We stepped through the code and found that the deployment of the WSP solution* failed with <b><span style="font-family: "Courier New", "Courier", monospace;">Microsoft.Dynamics.Framework.Portal.dll</span></b> being denied access to the GAC.<br />
<br />
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. <strong><span style="font-size:105%;">As it turned out an OS process had locked down the GAC because of critically low system resources.</span></strong><br />
<br />
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).<br />
<br />
Everything ran smoothly, and we could once again focus on other issues.<br />
<br />
* 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. :pThe Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com1tag:blogger.com,1999:blog-8072824882860488300.post-65503527187585475342010-09-24T14:51:00.002+02:002010-09-24T14:51:43.752+02:00Gearing up for HTML 5<a href="http://creativefan.com/fresh-and-useful-html5-tutorials-techniques-and-tricks/">http://creativefan.com/fresh-and-useful-html5-tutorials-techniques-and-tricks/</a>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-85240355015768749502010-09-24T09:50:00.008+02:002010-09-24T14:39:23.638+02:00The SharePointers' Guide to Angry Customer Questions!This is a list of various features not supported in this and that browser.<br />
<br />
<b>Consult this list at once!</b><br />
<br />
<a href="http://technet.microsoft.com/en-us/library/cc263526.aspx">http://technet.microsoft.com/en-us/library/cc263526.aspx</a><br />
<br />
Also, remember that you need to enable WebDAV for some of the Office interoperability functions.<br />
<br />
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.<br />
<br />
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.<br />
<br />
<a href="http://fmuntean.wordpress.com/2009/01/31/browsing-sharepoint-using-webdav-from-windows-2008/">Enabling Desktop Experience for WebDAV on Win2k8</a><br />
<br />
Note:<br />
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.<br />
<br />
Also, as the linked article suggest, Windows Vista needs to have its Web Folders service started.<br />
<br />
<a href="http://technet.microsoft.com/en-us/library/cc431377.aspx"> </a>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-42168682715582861282010-09-23T21:54:00.004+02:002010-09-24T14:06:27.322+02:00<h1><span lang="EN-US" style="mso-ansi-language: EN-US;">The Developer’s Guide to Configuration Files, Part One</span></h1><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;"><i>XML Configuration files for ASP.NET and IIS</i></span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;"> </span></div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Welcome to the .Config Jungle!</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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. </span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">Luckily <applicationname.exe.config> most are app.Config files for any given application. Still, there are other configuration files out there.</applicationname.exe.config></span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Web.Config</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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)</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Machine.Config</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">Base path: %systemroot%\Microsoft.NET\Framework\versionNumber\CONFIG\Machine.config</span></div><div class="MsoNormal"><br />
Examples for framework version 2 and 4:<br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">.NET 2.0: %systemroot%\Microsoft.NET\Framework\v2.0.50727\CONFIG</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">.NET 4.0: %systemroot%\Microsoft.NET\Framework\v4.0.30319\Config</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">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. ^^ </span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">An example:</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">ApplicationHost.Config</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">Changes to application pools and site bindings will be persisted in applicationHost.Config.</span></div><div class="MsoNormal"><br />
</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigs_13jdoskenwWaOkz-Q6racEU0AwYEAI3KaFWyrCKR1OKtHNUY8ZE0Mc2qGw9tLpIsRMTgkjJ59OVB7GEWGmsCUGIQEMWCrQhd8qcThCUKTSfi7z4ebCmdJLr24G3wwNWTCW0uqP5K4/s1600/piggy.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigs_13jdoskenwWaOkz-Q6racEU0AwYEAI3KaFWyrCKR1OKtHNUY8ZE0Mc2qGw9tLpIsRMTgkjJ59OVB7GEWGmsCUGIQEMWCrQhd8qcThCUKTSfi7z4ebCmdJLr24G3wwNWTCW0uqP5K4/s320/piggy.png" /></a></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">The first API is a native code C++ API, the second is a managed code .NET API.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">Want to change the modules loaded in the integrated pipeline? </span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">Well, you do that in web.Config, silly! </span><span lang="EN-US" style="mso-ansi-language: EN-US;"> </span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">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. </span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">For the purpose of demonstration I will lastly show the contents of an applicationHost.Config file.</span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">I will not show the whole shebang, but rather a site binding section for a SharePoint portal located under <system.applicationhost><System.ApplicationHost> and <Sites> </system.applicationhost></span><br />
<br />
<span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: x-small;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><br />
</limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><site name="SharePoint - 80" id="267757848" serverAutoStart="true"><br />
<application </limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #93c47d;">path</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b>="/"</b> applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000"><br />
<</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #93c47d;">virtualDirectory</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"> path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><span style="color: #b6d7a8;">/</span></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" physicalPath="C:\inetpub\wwwroot\wss\VirtualDirectories\80" /><br />
<<b>virtualDirectory</b> path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_vti_bin</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\isapi" /><br />
<virtualDirectory path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_layouts</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts" /><br />
<virtualDirectory path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_wpresources</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\wpresources" /><br />
<virtualDirectory path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_controltemplates</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\controltemplates" /><br />
</application><br />
<application path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_layouts/images</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000"><br />
<virtualDirectory path="/" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\images" /><br />
</application><br />
<application path="</limits></logfile></site></span></span></span></span><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><b><span style="color: #b6d7a8;">/_layouts/inc</span></b></limits></logfile></site></span></span></span><span style="color: #6fa8dc;"><span style="font-size: small;"><span style="font-family: "Courier New", "Courier", monospace;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00">" applicationPool="SharePoint_80_b25de04f0d124507b3da5c56bdfad000"><br />
<virtualDirectory path="/" physicalPath="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts\inc" /><br />
</application><br />
<bindings><br />
<binding protocol="http" bindingInformation=":80:" /><br />
</bindings><br />
<logFile logFormat="W3C" /><br />
<limits connectionTimeout="00:04:00" /><br />
</site></limits></logfile></site></span></span></span></span><span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: x-small;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><br />
</limits></logfile></site></span></span></span><br />
<span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: x-small;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"><br />
</limits></logfile></site></span></span></span></div><div class="MsoNormal"><span style="font-size: x-small;"><span lang="EN-US"> </span></span>Note the following:<br />
<ul><li>The configuration file binds a specific application pool to a site</li>
<li>This is where the SharePoint web site is mapped to it's SharePoint Root (12 HIVE) folders</li>
<li>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)</li>
<li>this is one of the myriad places in which timeout settings can be found</li>
</ul><span style="font-family: "Courier New", "Courier", monospace;"><span style="font-size: x-small;"><span lang="EN-US"><site name="SharePoint - 80" serverautostart="true"><logfile logformat="W3C"><limits connectiontimeout="00:04:00"> </limits></logfile></site></span></span></span><span style="font-size: x-small;"><span lang="EN-US"><br />
Trivia:</span></span><br />
<ul><li><span style="font-size: x-small;"><span lang="EN-US">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 <strike>rape</strike>, errr, <u>communicate with</u> 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!</span></span></li>
</ul></div><br />
<b>References:</b><br />
For the blog entries on configuration files I use the following literature:<br />
<ul><li>CLR via C#, by Jeffrey Richter</li>
<li>Professional ASP.NET 3.5 In C# and VB, by Evjen et al.</li>
<li><a href="http://msdn.microsoft.com/en-us/library/w0x726c2.aspx">msdn .NET Framework documentation </a></li>
</ul>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-59782787734789540762010-09-14T23:19:00.000+02:002010-09-14T23:19:30.627+02:00Opening PDFs in SharePoint 2010So, 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.<br />
<br />
<br />
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.<br />
<br />
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.<br />
<br />
If you wanna test your settings, you can run this Powershell script (you probably want to change the URLs)<br />
<br />
<span style="font-family: "Courier New", "Courier", monospace;">$site = Get-SPSite(“http://intranet")<br />
$web = $site.OpenWeb("/market")<br />
$list = $web.GetList("http://intranet/market/PDFs")<br />
Write-Host $list.browserfilehandling</span><br />
<br />
<span style="font-family: "Courier New", "Courier", monospace;"> </span><br />
If your settings were applied correctly, your list should be marked as Permissive, which will be echoed into the Powershell command line.The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-67169666144310343132010-09-06T15:09:00.000+02:002010-09-08T08:36:34.269+02:00Problem 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.<br />
<br />
<br />
I got a cryptic error telling me that one or more of SharePoint's field types had been installed incorrectly.<br />
<br />
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<siteurl>/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.</siteurl><br />
<br />
Thanks to the peeps at:<br />
http://www.go4answers.com/Example/error-starting-sharepoint-2010-beta-2-44876.aspxThe Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-86942234525437592082010-08-11T21:03:00.000+02:002010-08-11T21:03:24.276+02:00The cryptic AotNodeNotFoundException unearthedToday 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.<br />
<br />
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.<br />
<br />
When I shortened the name of the user control, and updated the AOT references accordingly, everything worked just fine.<br />
<br />
So keep those AOT node names trimmed!The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com1tag:blogger.com,1999:blog-8072824882860488300.post-31955734760323100802010-06-25T20:57:00.000+02:002010-06-25T21:13:22.825+02:00<div style="border-bottom: solid #4F81BD 1.0pt; border: none; mso-border-bottom-themecolor: accent1; mso-element: para-border-div; padding: 0cm 0cm 4.0pt 0cm;"><div class="MsoTitle"><span lang="EN-US"><span style="font-size: x-large;">The Zen of Creation</span></span></div></div><div class="MsoNormal"><span lang="EN-US" style="font-size: 8.0pt; line-height: 115%; mso-ansi-language: EN-US;">The Art of Creating New Records in Enterprise Portal</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">Not so in Enterprise Portal 2009!</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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! </span><br />
<br />
<span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Method number 1: The ASP.NET wizard tag</span></b></div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Method number 2: ASP.NET code behind insertion </span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">It is pretty easy to insert code in the code behind given the following format:</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span style="color: #2b91af; font-family: "Courier New"; font-size: 10pt;">DataSetView</span><span style="font-family: "Courier New"; font-size: 10pt;"> dsv = ds.GetDataSet().DataSetViews[0]; </span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: "Courier New"; font-size: 10pt;">if(dsv != null)</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: "Courier New"; font-size: 10pt;">{</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none; text-indent: 35.4pt;"><span style="color: #2b91af; font-family: "Courier New"; font-size: 10pt;">DataSetViewRow</span><span style="font-family: "Courier New"; font-size: 10pt;"> dsvr = dsv.AddNew();</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: "Courier New"; font-size: 10pt;"><span style="mso-tab-count: 1;"> </span></span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">Dsvr.BeginEdit();<br />
<span style="mso-tab-count: 1;"> </span>Dsvr.SetFieldValue(“TableField”, TxtBoxFromAspPage.Text);</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;"><span style="mso-tab-count: 1;"> </span>Dsvr.EndEdit();</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">}<br />
<br />
</span><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">Barring conversion issues this is all fine and dandy, but the method has some severe shortcomings.<br />
<br />
1. As already mentioned (Dynamics Ax) X++ to .NET data type conversion is truly painful.</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
<span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">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. </span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
<span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">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!</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">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.</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">Method number 3: DataSet method insertion</span></b></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">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.</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin; mso-no-proof: yes;">In C# the code looks like this:</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">Object[] paramList = new Object[2];<br />
paramList[0] = TxtProjectId.Text;<br />
paramList[1] = TxtSalesId.Text;</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt;">try</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;"><br />
{</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 35.4pt; margin-right: 0cm; margin-top: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">ProjTableDS.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call(<span style="color: #a31515;">"CreateNew"</span>, paramList);</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">}<br />
<span style="color: blue;">catch</span> (System.<span style="color: #2b91af;">Exception</span> exception)</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">{</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none; text-indent: 35.4pt;"><span lang="EN-US" style="color: #2b91af; font-family: "Courier New"; font-size: 10pt;">AxExceptionCategory</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;"> exceptionCategory;</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 35.4pt; margin-right: 0cm; margin-top: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt;">if</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;"> (!<span style="color: #2b91af;">AxControlExceptionHandler</span>.TryHandleException(<span style="color: blue;">this</span>, exception, <span style="color: blue;">out</span> exceptionCategory))</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 35.4pt; margin-right: 0cm; margin-top: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;">{</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; margin-left: 35.4pt; margin-right: 0cm; margin-top: 0cm; mso-layout-grid-align: none; text-autospace: none; text-indent: 35.4pt;"><span style="color: blue; font-family: "Courier New"; font-size: 10pt;">throw</span><span style="font-family: "Courier New"; font-size: 10pt;">;</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none; text-indent: 35.4pt;"><span style="font-family: "Courier New"; font-size: 10pt;">}</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: "Courier New"; font-size: 10pt;">}</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">And in X++ the dataset method code looks like this:</span></div><div class="MsoNormal"><br />
<span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt; line-height: 115%;">public</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;"> <span style="color: blue;">void</span> createNew(<span style="color: blue;">str</span> _projId, <span style="color: blue;">str</span> _salesId)<br />
{<br />
<span style="mso-spacerun: yes;"> </span>SalesId salesId = _salesId; //I will explain this below<br />
<span style="mso-spacerun: yes;"> </span>ProjId projId = _projId;<br />
<span style="mso-spacerun: yes;"> </span>;<br />
<span style="mso-spacerun: yes;"> </span>info(“ProjId = “ + _projId); //Not much action here<br />
<span style="mso-spacerun: yes;"> </span>info(“SalesId = “ + _salesId); //just to illustrate<br />
}</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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. </span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Method number 4: Proxy class insertion</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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. </span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">They are also very powerful.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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<drive>:\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.</drive></span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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#.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">This is how you might call such a proxy class:</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt; line-height: 115%;">using</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;"> Proxy = Microsoft.Dynamics.Portal.Application.Proxy;</span></div><div class="MsoNormal"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;">Proxy.DemoProxyClass proxy = Proxy.DemoProxyClass(AxSession.AxaptaAdapter);<br />
proxy.insert(parameter1, parameter2, …, parameterX);<br />
proxy.runImportantJob();<br />
</span><span lang="EN-US" style="mso-ansi-language: EN-US;"><br />
Invoking it from C# is kinda straight forward, but there are some issues:</span></div><div class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1.<span style="font-family: "Times New Roman"; font-size: 7pt; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;"> </span></span></span><span lang="EN-US" style="mso-ansi-language: EN-US;">Administrators will be dumbfounded by your need to have files under the SharePoint site virtual folder (IIS virtual folder). </span></div><div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">2.<span style="font-family: "Times New Roman"; font-size: 7pt; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;"> </span></span></span></div><div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;"><span lang="EN-US" style="mso-ansi-language: EN-US; mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;"><span style="font-family: "Times New Roman"; font-size: 7pt; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;"> </span></span></span><span lang="EN-US" style="mso-ansi-language: EN-US;"> 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.</span></div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Method number 5: FormDataSource insertion</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">This method is a variant of method number 2 really.</span></div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><br />
</div><div class="MsoNormal" style="line-height: normal; margin-bottom: .0001pt; margin-bottom: 0cm; mso-layout-grid-align: none; text-autospace: none;"><span lang="EN-US" style="color: #2b91af; font-family: "Courier New"; font-size: 10pt;">FormObjectSet</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt;"> formDataSource;<br />
formDataSource = SalesTable_DS.GetDataSet().DataSetRun.dataSource();<br />
formDataSource.create();</span></div><div class="MsoNormal"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;">formDataSource.initValue();<br />
</span><span lang="EN-US" style="mso-ansi-language: EN-US;"><br />
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).</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">As with the other means of contacting the dataset, this methodology is battle tested, works and is a good option. </span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="mso-ansi-language: EN-US;">Method number 6: .NET Business Connector insertion</span></b></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="mso-ansi-language: EN-US;">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.</span></div><div class="MsoNormal"><br />
</div><div class="MsoNormal"><span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt; line-height: 115%;">Axapta </span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;">ax = new <span style="color: blue;">Axapta</span>();</span></div><div class="MsoNormal"><span lang="EN-US" style="color: blue; font-family: "Courier New"; font-size: 10pt; line-height: 115%;">String</span><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;"> company = “DMO”;<br />
<span style="color: blue;">String</span> AOSInstance = “02”;<br />
<span style="color: blue;">String</span> AOSServerName = “THINK”;<br />
<span style="color: blue;">String</span> AOSPort = “49999”;<br />
<span style="color: blue;">AxaptaRecord </span>axRec;</span></div><div class="MsoNormal"><span lang="EN-US" style="font-family: "Courier New"; font-size: 10pt; line-height: 115%;">ax.Logon(company, "", AOSInstance + "@" + AOSServerName + ":" + AOSPort, ""); //log on to Axapta<br />
axRec = ax.CreateAxaptaRecord(“SalesLine”);<br />
axRec.set_Field(“PurchQty”, “1000”);</span><span lang="EN-US" style="mso-ansi-language: EN-US;"><br />
<br />
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.</span></div>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com3tag:blogger.com,1999:blog-8072824882860488300.post-14798385079228855962010-06-22T22:14:00.000+02:002010-06-24T09:35:49.267+02:00Filtering Enterprise Portal, the difference between UserFilter and standard rangesToday I discovered a subtle, but important, difference between the two of the filtering methods we can use in Enterprise Portal 2009.<br />
<br />
Given the following two sections of code in the Page_Load method of the ASP.NET code behind, and given the following:<br />
<br />
String TID = this.Page.Request.QueryString.Get("TID");<br />
if(!String.IsNullOrEmpty(TID))<br />
{<br />
//Method 1 or Method 2 here.<br />
}<br />
<br />
<b>Method 1: UserFilter</b><br />
<br />
<blockquote>DataSetView dsv = this.AxDataSource1.GetDataSet().DataSetViews[0];</blockquote><blockquote>dsv.UserFilter.ClearOpenFilter();</blockquote><blockquote>filterObject flt = new filterObject();</blockquote><blockquote><br />
</blockquote><blockquote>flt.name = "TIDFilter";</blockquote><blockquote><br />
</blockquote><blockquote>conditionType cndId = new conditionType();</blockquote><blockquote>cndId.@operator = operatorType.eq;</blockquote><blockquote>cndId.status = conditionStatus.open;</blockquote><blockquote>cndId.attribute = "TrvId";</blockquote><blockquote>cndId.value = TID;</blockquote><blockquote><br />
</blockquote><blockquote>flt.conditionCollection.Add(cndId); //add to filter </blockquote><blockquote> dsv.UserFilter.Add(flt); </blockquote><br />
<b><br />
</b><br />
<b>Method 2: Standard Ax QueryBuildRange filter</b><br />
<br />
<blockquote>DataSetView dsv = this.AxDataSource1.GetDataSet().DataSetViews[0];</blockquote><blockquote><br />
</blockquote><blockquote></blockquote><blockquote>Proxy.Query query = dsv.MasterDataSource.query();</blockquote><blockquote>Proxy.QueryBuildRange qbr = query.dataSourceNo(1).findRange(TableDataFieldMetadata.FieldNum(this.AxSession, "AdTrvTable", "TrvId"));</blockquote><blockquote>if (qbr == null)</blockquote><blockquote>qbr = query.dataSourceNo(1).addRange(TableDataFieldMetadata.FieldNum(this.AxSession, "AdTrvTable", "TrvId"));</blockquote><blockquote>qbr.value = TID;</blockquote><br />
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.<br />
<br />
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.<br />
<br />
If you are dependent on only having the appropriate records in your datasource you should use QueryBuildRange.<br />
<br />
If you are displaying different subsets of the datasource at different times you should use a UserFilter to reduce the number of server queries.The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-12648063918648995702010-05-28T11:15:00.000+02:002010-05-29T20:26:27.392+02:00A Note Concerning Web Reports through Ax and SSRSI had a chat with some colleagues yesterday and ventured into the "new" reporting framework, SSRS, that will gradually replace standard Ax reporting.<br />
<br />
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.<br />
<br />
To enable CSS background color printing, which at least the Ax Web reports use:<br />
<br />
Open Internet Explorer -> Options -> Advanced -> Printer -> Enable printing of background colors and images.<br />
<br />
(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).<br />
<br />
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.<br />
<br />
Believe me, the customers want to be able to print any report, and they want to be able to read it easily as well.<br />
<br />
The morale of the story?<br />
<br />
Enable that CSS formatting.The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-27344633012126790792010-05-06T09:55:00.000+02:002010-05-06T09:55:15.116+02:00<a href="http://daxguy.blogspot.com/2006/12/temporary-tables.html">http://daxguy.blogspot.com/2006/12/temporary-tables.html</a><br />
<br />
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. ;)The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com0tag:blogger.com,1999:blog-8072824882860488300.post-23123238146185459012010-05-05T22:07:00.000+02:002010-05-07T20:21:13.594+02:00Offer reports as downloadable PDF documents in your Dynamics Ax 2009 Enterprise PortalOne 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.<br />
<br />
By now one thing should be clear: Microsoft wants us all to switch to SSRS reporting.<br />
<br />
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.<br />
<br />
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.<br />
<br />
First, somewhere in your .NET user control*, preferably as the invoice details page loads (The Page_Load method for instance), call your dataset method.<br />
<br />
<br />
* In a webdefined control in Ax 3.0 Enterprise Portal (override the layout method instead).<br />
<br />
<blockquote><span style="font-family: "Courier New", "Courier", monospace;">private string InvoiceId;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> private string pdfUrl = "about:blank";</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> protected void Page_Load(object sender, EventArgs e)</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> {</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> if(!IsPostBack)</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> {</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> InvoiceId = Page.Request.QueryString.Get("IID"); </span><i><span style="font-family: "Courier New", "Courier", monospace;">// I passed the invoice id from the grid as a GET parameter</span></i><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> if(!String.IsNullOrEmpty(InvoiceId))</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> {</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> pdfUrl = "http://192.168.88.5:8080/Invoice_" + InvoiceId + ".PDF"; </span><i><span style="font-family: "Courier New", "Courier", monospace;">//obviously hard coding isn't the way to go</span></i></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">[...]</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">try</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> {</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> <b>CustInvoiceJour_DS.GetDataSet().DataSetRun.AxaptaObjectAdapter.Call("CreatePDF", InvoiceId);</b></span><i><span style="font-family: "Courier New", "Courier", monospace;"> //This is the call to our DataSet method that initiates the X++ code</span></i><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> InvoiceLink.NavigateUrl = pdfUrl; </span><i><span style="font-family: "Courier New", "Courier", monospace;">//<asp:hyperlink> object.</asp:hyperlink></span></i><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> }</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> catch (Exception)</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> {</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><span style="font-family: "Courier New", "Courier", monospace;"> </span><br />
<blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> throw;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> }</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> }</span></blockquote><br />
Here is the X++ DataSet method that generates the PDF invoice.<br />
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.<br />
NOTE 2: In Ax 3.0 this code must be in the Web Forms run method.<br />
<br />
<br />
<blockquote><span style="font-family: "Courier New", "Courier", monospace;"><b>public str CreatePDF(InvoiceId _invoiceId)</b></span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">{</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> Args parameters;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> ReportRun reportRun;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> //Declare SalesFormLetter and PrintJobSettings objects</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> SalesFormLetter salesFormLetter;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> PrintJobSettings printJobSettings;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> CustInvoiceJour cij;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> ;</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> SELECT firstonly cij</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> where cij.invoiceId == _invoiceId;</span></blockquote><br />
<blockquote><span style="font-family: "Courier New", "Courier", monospace;"> parameters = new Args(ReportStr(SalesInvoice));</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">printJobSettings = new PrintJobSettings();</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> printJobSettings.setTarget(PrintMedium::File);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> printJobSettings.preferredTarget(PrintMedium::File);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> printJobSettings.preferredMailFormat(PrintFormat::PDF); //Not necessary, but residue from the days this method sent an email instead.</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> printJobSettings.format(PrintFormat::PDF);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> printJobSettings.fileName(@"\\THINK\pdfhost\Invoice_" + cij.invoiceId + ".PDF"); <i>//Write to the IIS hosted virtual directory</i></span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> salesFormLetter = new SalesFormLetter_Invoice(false);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> </span><span style="font-family: "Courier New", "Courier", monospace;">salesFormLetter.updatePrinterSettingsFormLetter(printJobSettings.packPrintJobSettings());</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">parameters.caller(salesFormletter);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> parameters.parmEnum(PrintCopyOriginal::OriginalPrint);<i> //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.</i></span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> parameters.parmEnumType(enumnum(PrintCopyOriginal));</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> parameters.record(cij);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> reportRun = new ReportRun(parameters);</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> reportRun.init();</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> reportRun.run();</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"> return "";</span><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;"><br />
</span></blockquote><blockquote><span style="font-family: "Courier New", "Courier", monospace;">}</span><span style="font-family: "Courier New", "Courier", monospace;"> </span></blockquote><br />
Hope this helps someone out there!<br />
<br />
Thanks to Brandon George and other bloggers out there.<br />
<a href="http://dynamics-ax.blogspot.com/2006_01_01_archive.html">http://dynamics-ax.blogspot.com/2006_01_01_archive.html</a><br />
<br />
Also: Thank you to Justin Biggs<br />
<a href="https://community.dynamics.com/forums/t/32390.aspx">https://community.dynamics.com/forums/t/32390.aspx</a><br />
<blockquote><br />
</blockquote>The Ohttp://www.blogger.com/profile/11678269901942645149noreply@blogger.com1