By default, the WCF values for maxBufferSize and maxReceivedMessageSize are both 65536 (64K). If you send a request to a document service in Microsoft Dynamics AX and the message size exceeds this value, you’ll see a cryptic error that says something like: “The remote server returned an error: (400) Bad Request.” You may also see an error message that references the maxReceivedMessageSize property.
To fix this problem, increase the binding properties maxBufferSize and maxReceivedMessageSize. These properties are part of the <bindings> section in the app.config and web.config. Both parameters must be changed on the both the client side in the app.config and on the server side in the web.config. To change these properties, you can edit the config files directly or go to Basic > Setup > AIF > Services and click the Configure button to open the service configuration editor. Here are some links that cover this aspect of WCF:
MSDN: http://msdn.microsoft.com/en-us/library/ms733135.aspx
Blog: http://geekswithblogs.net/niemguy/archive/2007/12/11/wcf-maxstringcontentlength-maxbuffersize-and-maxreceivedmessagesize.aspx
These are the relevant binding sections of app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_FreeTextInvoiceService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="565536" maxBufferPoolSize="524288" maxReceivedMessageSize="565536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://dynamicsvm.contoso.com/MicrosoftDynamicsAXAif50/freetextinvoiceservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_FreeTextInvoiceService"
contract="FreeTxtInvoiceWebService.FreeTextInvoiceService"
name="BasicHttpBinding_FreeTextInvoiceService" />
</client>
</system.serviceModel>
</configuration>
These are the relevant binding sections of web.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBindingWindowsAuth"
maxBufferSize="565536"
maxReceivedMessageSize="565536">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<service behaviorConfiguration="serviceBehaviorConfiguration"
name="Microsoft.Dynamics.IntegrationFramework.Service.FreeTextInvoiceService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpBindingWindowsAuth"
bindingNamespace="http://schemas.microsoft.com/dynamics/2008/01/services"
contract="Microsoft.Dynamics.IntegrationFramework.Service.FreeTextInvoiceService" />
</service>
In addition to changing these properties, you might also want to look at the transportMode property if your messages are very large. By default, this property is set to Buffered for the document services in AX. More relevant links about this property are:
MSDN: http://msdn.microsoft.com/en-us/library/ms731913.aspx
MSDN: http://msdn.microsoft.com/en-us/library/ms789010.aspx
MSDN Forum: http://social.msdn.microsoft.com/forums/en-US/wcf/thread/f00037f1-02e1-4e1c-b6ed-03fe134f3460/
MSDN Blog: http://blogs.msdn.com/drnick/archive/2006/03/31/565558.aspx
There are many considerations when it comes to message size and services such as limits in IIS, WCF configuration options, etc. Let me know if this resolves your message size issues or if you come across any other tips that worked in your particular installation.
The AIF/BizTalk white paper has been updated to provide hands-on instructions for reading, updating, and deleting a sales order from BizTalk. You can find it here. This white paper is an extension of the Microsoft Dynamics AX 2009 AIF BizTalk Adapter Configuration white paper. This paper is designed for BizTalk developers and Microsoft Dynamics AX system administrators who are responsible for installing, configuring, and troubleshooting integrations with external systems using AIF and BizTalk Server.
Version: Dynamics AX 2009.
Disclaimer
All code used below is meant for illustration purposes only and is not intended for use in production. The following disclaimer applies to all code used in this blog:
Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. USE AND REDISTRIBUTION OF THIS CODE, WITH OR WITHOUT MODIFICATION, IS HEREBY PERMITTED.
Overview
In certain scenarios, X++ classes need to be exposed for consumption by external applications, for example, through WCF web services or through MSMQ. Custom services, which have been introduced with Microsoft Dynamics AX 2009, are intended exactly for that purpose.
However, since hardly any assumptions can be made about the purpose or semantics of these custom services or about the structure of the parameter types used by the published service operations, creating a custom service involves a little more work that creating a document-centric service with AIF’s Create document service wizard from a Dynamics AX query. In order to implement a custom service, you need to:
o Write a service implementation class
o Create a service interface
o Implement data objects – if necessary
The following sections walk you through a simple example of how you can build a Dynamics AX service from an X++ class named MyDataObjectService. Once you have encapsulated the business logic you would like to expose, you can use AIF’s standard tools and infrastructure to publish the service through the (supported) transport of your choice.
Writing a Service Implementation Class
A service implementation class is an X++ class; service implementation classes need not implement any X++ interfaces or extend any X++ super classes. The class definition of a sample service implementation class MyDataObjectService may look like this:
public class MyDataObjectService
{
}
One of MyDataObjectService’s service operations may look as shown in the following code snippet:
public MyDataObject CreateMyDataObject(str _s)
{
MyDataObject mdo; // see below for a definition of MyDataObject
;
mdo = new MyDataObject();
mdo.parmMyString(_s);
// do something with ‘mdo’, for instance persist it...
return mdo;
}
The input and return parameters of service operations must be either of primitive X++ types, or they must be instances of X++ classes that implement the X++ interface AifXmlSerializable.
Creating a Service Contract
In order to create a new service contract, we need to create a new AOT node under the AOT Services node. Let’s name the service contract MyDataObjectService, just like the service implementation class.
The newly created service contract has a few properties that need to be initialized before the service can be consumed by external clients:
o Service implementation class: MyDataObjectService
o Security key: Each service should have its own security key with the same name as the service, for example, MyDataObjectService; should have parent key Services in a functional area (for example, Accounts Receivable)
o Namespace (optional): XML namespace that should be used in the WSDL can be specified
o External name (optional): External name of the service
Finally, the service operations need to be added to the service contract; see for instance product documentation for additional details.
Implementing Data Objects
Service operations can automatically use primitive X++ types (such as int, str, etc.) as types for input and return parameters. X++ classes that are intended to be used as data objects – as input or return parameters for service operations – must implement the interface AifXmlSerializable. See the following snippet for an example:
public class MyDataObject implements AifXmlSerializable
{
str myString;
// more fields...
#define.MyDataObjectNS ('http://schemas.contoso.com/samples/MyDataObject')
#define.MyDataObjectRoot (‘MyDataObject’)
}
Note that arrays or any other X++ data structures are not primitive types, even if they only contain data of primitive types; thus, data objects must be defined for such constructs as well.
This is necessary to define the custom serialization to and the deserialization from XML for that class. Note that the methods serialize() and deserialize() must be inverse functions, since they use the same XML schema definition. In other words, it must be possible to deserialize an XML document that was created by serializing a data object and vice versa.
The code snippets below are examples for the implementation of these methods. For additional information about any of the implemented methods refer to the product documentation.
The method serialize() defines the serialization of the data object to XML. The code for serializing the class MyDataObject (as defined above) to XML may look similar to this:
AifXml serialize()
{
str xml;
XmlTextWriter xmlTextWriter;
;
#Aif
xmlTextWriter = XmlTextWriter::newXml();
// turn off indentation to reduce file size
xmlTextWriter.formatting(XmlFormatting::None);
// initialize XML document
xmlTextWriter.writeStartDocument();
// write root element
xmlTextWriter.writeStartElement2(#MyDataObjectRoot, #MyDataObjectNS);
// write custom data
xmlTextWriter.writeElementString('MyString', myString);
// more fields...
// serialize XML document into XML string
xmlTextWriter.writeEndDocument();
xml = xmlTextWriter.writeToString();
xmlTextWriter.close();
return xml;
}
The method deserialize() defines the deserialization of a data object from XML. The code for deserializing an instance of the class MyDataObject (as defined above) from XML may look like this:
void deserialize(AifXml xml)
{
XmlTextReader xmlReader;
;
xmlReader = XmlTextReader::newXml(xml);
// turn off Whitespace handling to avoid extra reads
xmlReader.whitespaceHandling(XmlWhitespaceHandling::None);
xmlReader.moveToContent();
while ((xmlReader.nodeType() != XmlNodeType::Element) && !xmlReader.eof())
xmlReader.read();
xmlReader.readStartElement3(#MyDataObjectRoot, #MyDataObjectNS);
if (!xmlReader.eof() && xmlReader.isStartElement())
{
myString = xmlReader.readElementString3('MyString', #MyDataObjectNS);
// more fields...
}
xmlReader.readEndElement();
xmlReader.close();
}
In X++, parm methods are used to define properties on a class. In data objects, all fields that are used for serialization or deserialization must be accessible through parm methods. Moreover, they must be optional and thus have a default value. Example:
public str parmMyString(str _myString = ‘’)
{
if (!prmisdefault(_myString))
{
myString = _myString;
}
return myString;
}
The method getRootName() returns the root name used for deriving names for service artifacts. An example for the implementation:
public AifDocumentName getRootName()
{
return #MyDataObjectRoot;
}
The method getSchema() returns the XML schema definition (XSD) that is used for serializing and deserializing the data object.
public AifXml getSchema()
{
str schema =
@'<?xml version="1.0"?>
<xsd:schema xmlns="http://schemas.contoso.com/samples/MyDataObject"
targetNamespace="http://schemas.contoso.com/samples/MyDataObject"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xsd:complexType name="MyDataObjectType">
<xsd:sequence>
<xsd:element name="MyString" type="xsd:string" />
<!-- more fields... -->
</xsd:sequence>
</xsd:complexType>
<xsd:element name="MyDataObject" type="MyDataObjectType"/>
</xsd:schema>'
;
return schema;
}
XML schemas are used for validation, for example, to avoid the processing of invalid request messages. As a best practice, you should always use a modeling tool or an XML/XSD editor for generating the XML schema definitions rather than hand-crafting them.
Discovering Custom Services
Open the form Services (by navigating to Basic > Setup > Application Integration Framework > Services) and click Refresh. Once the form has refreshed its contents, the MyDataObjectService service displays along with the other services. It’s ready for use and can now be published for consumption by external service clients (see product documentation for details).
Version: Dynamics AX 2009.
Problem
You have published your Dynamics AX services as Windows Communication Foundation (WCF) web services through Microsoft Internet Information Services (IIS), running on Windows Server 2003 or on Windows Server 2008. When you try to browse files with the extension '.svc' you get an error message of type 404.3 with a description similar to "The page you are requesting cannot be served because of the extension configuration".
'.svc' files are WCF service artifacts; they are similar to '.asmx' files used in older web service implementations. You can browse '.svc' files for instance by pointing your browser to a URL similar to http://myhost/MicrosoftDynamicsAxAif50/CustomerService.svc; please refer to the product documentation for details about determining the actual URL for a specific service.
Possible Cause
I ran into this issue on test machines where I had installed IIS after installing .NET Framework 3.0, 3.5 or Windows SDK for Longhorn Server. It turned out that WCF - and the file extensions it supports (including .svc) - had not been registered properly with IIS and ASP.NET.
Solution
I used the ServiceModel Registration Tool (see [1]) to reregister WCF with IIS and ASP.NET and ran it on the machine to which I had deployed my Dynamics AX web services. The command options that worked for me were:
ServiceModelReg.exe -i -x
Note that this procedure is also documented in the IT Pro documentation.
Related Links
[1] http://msdn.microsoft.com/en-us/library/ms732012.aspx
Version: Dynamics AX 4, Dynamics AX 2009.
Problem
When you host Dynamics AX web services on Microsoft Internet Information Services (IIS) v6 you might see an error message similar to the one shown in Figure 1 at runtime.
Possible Cause
This may happen when IIS is configured to use Integrated Windows authentication to authenticate client requests and thus needs to exchange Negotiate security headers with the client.
Resolution
IIS needs to be configured to support both the Kerberos protocol and the NTLM protocol for network authentication; see http://support.microsoft.com/kb/215383 for details.
Version: Dynamics AX 4 SP2.
In Dynamics AX 4 SP2, basic framework support for update and delete functionality has been added to the pre-existing framework support for create, read and find functionality. This blog illustrates how the pre-existing functionality as well as the new update and delete functionality can be consumed from a sample C# web service client. The same functionality is accessible through any other transport supported by AIF, including the BizTalk adapter, the MSMQ adapter and the file system adapter.
The samples below use the Axd class AxdCustomer, which ships as part of Dynamics AX 4 SP2 and provides update and delete functionality out of the box. [For a full program sample see attachment "CustomerServiceTest.cs".]
0. Disclaimer
· All code used below is meant for illustration purposes only and not intended for use in production. The following disclaimer applied to all code used in this blog:
Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. USE AND REDISTRIBUTION OF THIS CODE, WITH OR WITHOUT MODIFICATION, IS HEREBY PERMITTED.
1. Prerequisites
· Install and configure Dynamics AX 4 SP2
· Install and configure AIF Web Services according to AIF documentation; this includes:
o IIS configuration (to host generated AIF web services)
o Configuration of AIF web site (AIF form "Web sites")
o Etc.
As a result of this configuration it must be possible to generate and deploy Dynamics AX web services.
2. Configure & Generate AIF Web Service "CustomerService"
· In the table on the “Action” form (Basic -> Setup -> Application Integration Framework -> Action), check “Enabled” and “Web Method Enabled” in the rows for the following actions:
o createListCustomer
o readListCustomer
o deleteListCustomer
· On the “Action” form, click “Generate” to generate web service(s)
· Verify that the WSDL for CustomerService is available (e.g. http://<host>:<port>/DynamicsWebService/CustomerService.asmx?wsdl; for details see AIF documentation)
3. Configure Additional AIF Parameters
· Create Local Endpoint "Contoso" (Basic -> Setup -> Application Integration Framework -> Local Endpoints)
· Create and configure Endpoint "NorthwindTraders" (Basic -> Setup -> Application Integration Framework -> Endpoints)
o Settings (for details and additional options for endpoint configuration see AIF documentation):
§ Name: “Nortwind Traders”
§ Constraints: No constraints
§ User: Valid Dynamics AX user
§ Local Endpoint: "Contoso"
o Save and activate Endpoint “NorthwindTraders”
o Configure action policies (enable createListCustomer, readListCustomer, deleteListCustomer)
o Configure data policies for these three actions (allow all data fields: Set -> Enable All)
4. Create Sample C# Web Service Client (Visual Studio 2005)
· Open Visual Studio 2005
· Create project “WebServiceTests” (console application)
· Add web reference to CustomerService to project (use URL for CustomerService WSDL, see above)
· Implement code to exercise customer service API (adjust names to actual names):
o Use code similar to the following snippet to create customer service proxy:
// assumes namespace “WebServiceTests”
using WebServiceTests.CustomerService;
...
CustomerService.CustomerService service =
new CustomerService.CustomerService();
if (null == this.service) {
throw new Exception(
"Cannot instantiate service.");
}
service.UseDefaultCredentials = true;
o Use code similar to the following snippet to a create customer (this has not changed in Dynamics AX 4 SP2).
AxdCustomer customer = new AxdCustomer();
// create & populate customer record (w/demo data)
customer.CustTable = new AxdEntity_CustTable[1];
customer.CustTable[0] = new AxdEntity_CustTable();
customer.CustTable[0].CustGroup = "40";
customer.CustTable[0].Name = "Customer";
// consume AX web service to create customer
EntityKey[] entityKey = null;
entityKey = service.createListCustomer(
getDocumentContext(), customer);
o Use code similar to the following snippet to read the newly created customer record (this has not changed in Dynamics AX 4 SP2):
customer = service.readListCustomer(
getDocumentContext(), entityKey);
o Use code similar to the following snippet to update name and zip code of the newly created customer record (this is new functionality, introduced on Dynamics AX 4 SP2).
Notes:
§ In Dynamics AX 4.0 SP2, create actions can be overloaded with update functionality; in other words, create and createList actions can be used to update existing records. Explicit update actions are not generated by the Axd wizard nor are they shipped as part of any Dynamics AX 4.0 Axd classes out of the box. For Axd classes that ship out of the box, create and createList actions are used to implement update functionality instead (where updates are supported).
§ Update actions are implemented with document-centric service semantics; they assume the “whole documents” to be submitted as part of update requests – in the sample these documents are customer documents. Fields that are not set in the document will be reset in Dynamics AX to the equivalent of the “null” value for the respective type. If this is not desirable, it is recommended to read the whole document before updating it. Also note that the definition of a “whole document” depends on the respective endpoint that submits the document as part of an update request. Fields that the endpoint is not allowed to send as part of the update request (as per data policy definition), need not/cannot be sent as part of update requests. Thus, it is further recommended to synchronize the data policies for read and update (i.e. create) actions.
// modify customer name: append name with “X”
customer.CustTable[0].Name += "X";
// set customer zip code (using demo data)
customer.CustTable[0].ZipCode = "DK-3600";
// apply changes
service.createListCustomer(getDocumentContext(),
customer);
o Use code similar to the following snippet to delete the newly created customer record (this is new functionality, introduced on Dynamics AX 4 SP2).
service.deleteListCustomer(getDocumentContext(),
entityKey);
5. Try it out...
Hello everybody,
The AIF team is excited to announce this new blog for discussing issues related to integrating Dynamics AX through the Application Integration Framework (AIF) and Dynamics AX services.
Version: Dynamics AX 4.
1 Motivation
In today's world of complex business processes, it is not always desirable or possible for companies to implement or customize all aspects of their business applications in-house. At the same time, service providers offer a wide variery of solutions for specific business problems, such as tax services (sales tax, etc), regulatory services (OFAC, etc), unified messaging (instant messaging, phone notifications, etc), eCommerce (e.g. Amazon, EBay, and many others), etc.
Businesses benefit from outsourcing areas which are not their core competencies to such external service providers. They can rely on the service providers' expertise in the respective area. and don't have to worry about currency of the data, reliability of the service, etc. Various service providers implement different business models and SLAs around the services they offer.
Nowadays, one of the most popular, low-cost ways of integrating external applications with an enterprise application (such as Dynamics AX) is through web services. This blog walks through the steps necessary for consuming external web services from within the Dynamics AX 4 application.
2 Disclaimer
All code used below is meant for illustration purposes only and not intended for use in production. The following disclaimer applies to all code used in this blog:
Copyright (c) Microsoft Corporation. All rights reserved. THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. USE AND REDISTRIBUTION OF THIS CODE, WITH OR WITHOUT MODIFICATION, IS HEREBY PERMITTED.
3 Process
3.0 Prerequisites
Visual Studio (in this example, we use version 2005)
3.1 Create proxy classes from the WSDL
a. Open Visual Studio
b. Create a new project of type "Class Library" with the name "ClassLibrary1" (default name)
c. Right-click on the project and select "Add Web Reference"
i. For the URL enter: http://soap.search.msn.com/webservices.asmx?wsdl
ii. For the reference name enter: LiveSearch
iii. Click "Add Reference"
d. In the project's properties, under "Signing", check "Sign the assembly" (assembly must be strong-named)
e. Build the solution and verify that a file named "ClassLibrary1.dll" appears in Visual Studio's output folder
f. Add the DLL proxy to the GAC
i. Open a Visual Studio command prompt
ii. Navigate to the folder that contains ClassLibrary1.dll
iii. In the command prompt, type "gacutil /i ClassLibrary1.dll"
Note: Adding this assembly to the GAC is not necessarily recommended and is intended for testing;
an alternative approach for trusted assemblies would be to move the assembly into the .NET Assembly directory for Microsoft Dynamics AX (refer to the product documentation to locate that directory)
3.2 Add the proxy as an AX reference
a. Open the Dynamics AX client
b. Open an AOT instance
c. In AOT, right-click on the AOT node “References”
d. Select “Add Reference”
e. Navigate to the folder that contains "ClassLibrary1.dll" and add the DLL as a reference
f. Verify that the proxy DLL now appears as a child under the AOT node “References”
3.3 Use the proxy from X++ (through CLR Interop)
a. Refer to CLR documentation for details on how to access .NET assemblies from X++
b. Create a new job "LiveSearchTest" and discover the .NET generated proxy class classes with IntelliSense
c. Instantiate the (.NET) proxy just as with any other class:
e.g. “ClassLibrary1.LiveSearch.MSNSearchService service = new ClassLibrary1.LiveSearch.MSNSearchService();”
d. See attachment for a sample job for consuming the LiveSearch web service from X++. Note that you will need an application ID that you can obtain at http://dev.live.com/search.