Tuesday, June 29, 2010

AX .NET Business Connector – How to Open Multiple Company Connections Simultaneously

As part of a recent project we needed to access the .NET Business Connector in Dynamics AX through a component (in this case for data lookup in BizTalk’s Business Rule Engine).  Because we were going to have many messages flowing through in a short period of time all requiring evaluation of business rules based on AX data, we intended to create some helper classes to enable the reuse of AX connections and to cache data used in the rules.  Our challenge was that messages were destined for different companies in AX.  If you have two orchestrations simultaneously evaluating these rules which in turn simultaneously open connections to AX you run into a bit of trouble.

Suppose you have two Microsoft.Dynamics.BusinessConnectorNet.Axapta objects (DAX1, DAX2), one each for different companies:

System.Net.NetworkCredential credential = new System.Net.NetworkCredential("<BC_USER>", "<BC_PASSWORD>", "<BC_DOMAIN>");
DAX1.LogonAs("<AX_USER>”, "<BC_DOMAIN>", credential, "<COMPANY_1>", "", "<COMPANY_1>@<AOS>:<PORT>", "");
DAX2.LogonAs("<AX_USER>”, "<BC_DOMAIN>", credential, "<COMPANY_2>", "", "<COMPANY_2>@<AOS>:<PORT>", "");

When you reach the second LogonAs statement, the logon fails with an exception of type LogonSystemChangedException.  ("The logon failed because the logon parameters do not match those currently being used in Business Connector").

The following blog post gives a good description of the Business Connector’s intended use, which is essentially to act as a client:

http://blogs.msdn.com/floditt/archive/2008/07/24/the-net-business-connector-bc-net-and-the-iis.aspx

At first it seemed there was no way to do this.  We opened a ticket with Microsoft and as a short term solution, installed the AX Client on the BizTalk server so that we could use the overload of LogonAs that takes a path to an .axc file with connection details.  It turns out this works, however you have the overhead of installing the AX client on the BizTalk server and creating .axc files for BizTalk.  We didn’t like this idea also because of issues with maintaining the .axc files and the slower performance of the Business Connector using the Client to read the .axc file.

Ultimately Microsoft came back with a resolution.  It turns out that the parameter for the object server (second last parameter) was incorrect.  Unfortunately in many code samples and on MSDN, this parameter is defined as "Company1@AosInstance:PortNumber”.  The first part of this should actually be the Instance Name (by default “DynamicsAx”) and the part after the @ sign is the Server name.  A better example might be “DynamicsAx@AOSSERVER01:2712”.

Changing the above code to use Instance names works:

System.Net.NetworkCredential credential = new System.Net.NetworkCredential("<BC_USER>", "<BC_PASSWORD>", "<BC_DOMAIN>");
DAX1.LogonAs("<AX_USER>”, "<BC_DOMAIN>", credential, "<COMPANY_1>", "", "<INSTANCE_NAME>@<AOS>:<PORT>", "");
DAX2.LogonAs("<AX_USER>”, "<BC_DOMAIN>", credential, "<COMPANY_2>", "", "<INSTANCE_NAME>@<AOS>:<PORT>", "");

So now we can open two connections to AX simultaneously with different companies in the same object instance.

Monday, November 23, 2009

BizTalk BAM Authentication Error

I ran into this issue today on my VPC running BTS2009 on WIN2K8/IIS7:

HTTP Error 401: The requested resource requires user authentication

I found the following blog posting which helped me resolve the issue:

http://sharepointservices.wordpress.com/2009/04/28/http-error-401-the-requested-resource-requires-user-authentication-in-iis7/

Indeed turning off kernel-mode authentication fixed it but I too am concerned with the validity of this fix.  For now I am not too concerned because it is a local dev VPC so I will see if the new production servers will have the same issue.


Thursday, September 10, 2009

Dynamics AX AIF Adapter Progress

Well after some initial struggles with locked channels (never resolved this, just built a new VPC!!) I have both the AIF tutorial and some PoCs working. 

Just to clarify that in a previous post I mentioned the latest issue of BizTalk HotRod which has an article on the adapter.  While most of the configuration is the same, it’s important to note that the article uses Dynamics AX 4.0, not 2009.

A particular challenge I recently had to overcome was regarding security on the send port.  I had setup and tested the AIF tutorials using a Proxy User (providing an AX user account/password right in the send port).  This worked great.  However, once I wanted to enable another document service (namely LedgerPurchaseInvoiceService) and follow an identical approach (correctly assigning a Data Policy, verifying my endpoints) I continued to get errors in the event log indicating permission was denied.  I was using the administrator account as both the BC service account and the gateway user.  I looked at a few other reports of similar issues but couldn’t get it working.

Some colleagues had mentioned that security configuration with AIF can be a challenge, and particularly that using anything other than the Host User configuration can sometimes just not work.  I remain convinced that it should work, I’m just not doing something right.  However I didn’t have a lot of time to debug it and/or open a PSS ticket, so I proceeded to change the send port to use the identity of the Host User (ensuring the service account was a user in DAX with the right permissions) and it worked. 

I am still concerned about the cause of issue with the Proxy User configuration, but the reality is that it likely makes more sense to use the host instance account for authentication regardless as it simplifies deployment (no password to maintain) and you can keep whatever degree of account isolation you need (one account for all Host Instances, or one per endpoint/service).

OutOfMemoryException on MemoryStream in Pipeline Component

On one of my projects, I was using a custom pipeline component to decompress a file in a send pipeline (*see side note on design decision at end of post).  In the production environment, we began to see pipeline failures caused by mscorlib OutOfMemoryExceptions in the pipeline component.  It was happening sporadically, so at first was not clear what was happening, though we knew the issue was occurring with growing frequency, roughly in correlation to the increase in the size of the file (the compressed file was a statement of account balances which grew as the number of accounts grew).

As many BizTalkers do, I use the SharpZipLib library for Zip compression (see Pro BizTalk 2006 by Dunphy and Metwally for a great example of this) and was taking the approach of loading the zipstream into a MemoryStream object.  The exception was being thrown in my loop which copied 4K segments into the stream.

After consulting my trusted advisor I saw a few discussions related to the need to provide a contiguous segment of memory for a MemoryStream to work.

I quickly inferred the likely culprit: I was trying to load data into a MemoryStream without the OS knowing how much memory to allocate it, so in many cases it was allocating a contiguous block that would not be enough for the uncompressed file.

Short term fix:

For the time being, I have simply updated my pipeline component to declare the size of the MemoryStream up front based on the zipstream’s Size property which is the number of bytes of the fiie uncompressed.

DANGER: the Size property is a Long, whereas you can only instantiate a MemoryStream with an Int32 (of course from the 2GB memory limit for 32-bit processes).  Knowing the file size will not grow beyond 1GB uncompressed, I have squished the long into an int, which of course is terrible.  Hence a long-term fix:

Long term fix:

Though I have yet to implement this, the sensible approach is instead to use something like the VirtualStream which will offload data to the filesystem if a stream exceeds a configured size, saving your poor BTSNTSvc.exe

Indeed hardware should never be used to mask up bad code, but it’s interesting to consider that this issue would likely not have arisen if it had been deployed on a 64-bit OS, which we can hopefully encourage all clients to do in the future.

 

* as a side note, the unzip was done in a send pipeline, in opposition to the usual approach of decompressing a file in a receive pipeline because the contents of the file were not needed.  All we wanted to do was route based on the file name, so by delaying the decompression we only needed to load a 16MB compressed file to the messagebox instead of a 700MB uncompressed file.

Thursday, August 27, 2009

Relative .snk file paths in Visual Studio 2008

I’m sure someone has already blogged on this, but as a reminder to myself and others, in Visual Studio 2008 it is not as straightforward to provide a relative path for a shared .snk file (for instance when signing multiple BizTalk projects with the same key file).

In the past, you could simply type in the path to the snk file in the project properties. Now this field is a dropdown and selecting browse will only let you find and copy the file to the root of the project.

image

To get around this, you can edit the project file (in the case of BizTalk, the .btproj). The following is an example where the .snk is both included as a shortcut in the project (not a copy) and uses the .snk for signing:

(Note: this project is the updated project schema for BizTalk 2009, so don't try to use this approach for BTS2006+VS2005)

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6092B853-D9B4-4C5E-B480-746703C74E80}</ProjectGuid>
<ProjectTypeGuids>{EF7E3281-CD33-11D4-8326-00C04FA0CE8D};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>library</OutputType>
<GenericProcessing>true</GenericProcessing>
<RootNamespace>Test.Orchestrations</RootNamespace>
<AssemblyName>Test.Orchestrations</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<BpelCompliance>True</BpelCompliance>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\Test.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
<Reference Include="System.Configuration">
<Name>System.Configuration</Name>
</Reference>
<Reference Include="Microsoft.BizTalk.Pipeline">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="Microsoft.BizTalk.DefaultPipelines">
<Name>Microsoft.BizTalk.DefaultPipelines</Name>
</Reference>
<Reference Include="Microsoft.BizTalk.GlobalPropertySchemas">
<Name>Microsoft.BizTalk.GlobalPropertySchemas</Name>
</Reference>
<Reference Include="Microsoft.BizTalk.TestTools">
<Name>Microsoft.BizTalk.TestTools</Name>
</Reference>
<Reference Include="Microsoft.XLANGs.BaseTypes">
<Name>Microsoft.XLANGs.BaseTypes</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\Test.snk" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\BizTalk\BizTalkC.targets" />
</Project>

Just save the file and VS will reload the project showing “..\Test.snk” as the location of the strong name key file.