Consuming Web Services using Microsoft Dynamics AX

(For more resources related to this topic, see here.)

Installing Visual Studio tools

Part of the coding that is needed to consume an external service is performed in Visual Studio. This is why we must install both Visual Studio 2010 and Visual Studio Tools for Microsoft Dynamics AX 2012 before we can create Visual Studio projects and add them to the AOT. Although you can develop applications that integrate with Microsoft Dynamics AX 2012 using other versions of Visual Studio, such as Visual Studio 2012, Visual Studio Tools are only available for Visual Studio 2010.

To install Visual Studio tools, perform the following steps:

  1. Run the Microsoft Dynamics AX 2012 setup.

  2. Go to the Install section and select Microsoft Dynamics AX Components.

  3. Click on the Next button to move to the next screen and select Add or modify existing components.

  4. Look under the Developer Tools node and select Visual Studio Tools.

  5. Go through the rest of the setup wizard to complete the installation process.

Installing Visual Studio Tools will add the following extensions to Visual Studio:

  • The Application Explorer option that is available in Visual Studio by navigating to View | Application Explorer. Enabling it will display the AOT in Visual Studio.

  • Two new templates that are available when you create a new project in Visual Studio — Report Model and EP Web Applications.

  • An option to add Visual Studio projects to the AOT. This is the option we're interested in when consuming web services.

Visual Studio development

When consuming a service, the first thing you need to do is create a reference to the service. As this can no longer be done in Microsoft Dynamics AX, we have to use Visual Studio. So, we'll do that, but first we'll examine the service that we are going to consume.

Introducing the USA zip code service

To show you how Microsoft Dynamics AX 2012 enables developers to consume web services, we are going to use an example zip code service. This service is available on the website of RESTful web services:

In the zip code service, we have two operations available to use when referencing the Windows Communication Foundation (WCF) version; these are as follows:

  • GetPostCodeDetailByPostCode: This operation takes a zip code as a parameter and returns a PostalCode data contract with all of the information about PostalCode we searched for
  • GetPostCodeDetailByPlaceName: This operation takes names as parameters and also returns a PostalCode data contract with the information needed

Creating the Visual Studio proxy library

In Microsoft Dynamics AX 2012, Visual Studio projects can be contained in the AOT. This enables us to use Visual Studio to create a class library project and add it to the AOT. The advantage is that Visual Studio deals with the service reference. It uses the SvcUtil tool to create the proxy client and generate the types that are needed to consume the services.

Perform the following steps to create a Visual Studio class library project:

  1. In Visual Studio, navigate to File | New | Project.
  2. In the New Project window, select Visual C# and select the Class Library project type.
  3. In the Name textbox, give the project a name and click on OK.

These steps are illustrated in the following screenshot:

Adding a service reference

Next, we will create a service reference to the USA zip code service. To do this, perform the following steps:

  1. Locate the References node in the project.

  2. Right-click on the References node and select Add Service Reference… to open the Add Service Reference window.

  3. In the Address drop-down box, specify the following address for the service: Then, click on Go. The address is queried and the two operations mentioned above are listed. You can expand to the USAZipCodeService node to view the service operations.

  4. In the Namespace dialog box, specify the namespace that you want to use: USAZipCodeServiceRef. The Add Service Reference window will look like the following screenshot:

  5. Click on OK. The service will be added to the Service References node.

  6. Delete the Class1.cs class as we will not need it.

  7. To add the project to the AOT, right-click on the project and then click on Add DynamicsAxServices.WebServices.ZipCode in the AOT.

  8. After the project has been added to the AOT, you can specify the deployment options. In the properties of the project, set Deploy to Client and Deploy to Server to Yes.

  9. Finally, right-click on the project and click on Deploy.

X++ development

The Visual Studio project and its output has been added to the AOT, so the first stage of development is now complete. You can leave Visual Studio and switch to Microsoft Dynamics AX 2012. The project has been added to the Visual Studio Projects node in the AOT. As we have used C#, the project will be in the C Sharp Projects node.

Look for the DynamicsAxServices.WebServices.ZipCode project and expand some of the nodes to inspect it. It should appear as shown in the following screenshot:

As you can see in the preceding screenshot, this is divided into the following two main components:

  • The Project Content node contains the actual C# project source such as properties of the project, the service references, an app.config file, and C# source files

  • The Project Output node contains the assemblies that will be deployed, taking into account the deployment options

In order to use the assemblies that have been created and stored in the AOT, we'll have to deploy them. Let's look at the options that are available.

Deploying managed code

When we create a project in Visual Studio and add it to the AOT, the following deployment options are available:

  • Deploy to Server
  • Deploy to Client
  • Deploy to EP

In our earlier example, we enabled deployment on the client and to the server because these are important in the context of services.

Deploy to Server

When you have enabled deployment to the server, the output of the Visual Studio project will be copied to the VSAssemblies subfolder in the bin folder of the Application Object Server (AOS) directory. The default path is C:\Program Files\Microsoft Dynamics AX\60\Server\\Bin\VSAssemblies. After you have deployed assemblies to the server, you should restart the AOS so that they are loaded.

Hot swapping

When hot swapping is enabled on the AOS, restart is not needed after deployment. This feature is added for the convenience of developers but is not recommended for a production environment. For more info, check out the following article on MSDN: How to: Enable Hot Swapping of Assemblies (

Deploy to Client

When you have enabled a deployment to the client, the output of the Visual Studio project will be copied to the following folder on the client: %localappdata%\Microsoft\Dynamics Ax\VSAssemblies. You may have to restart the Microsoft Dynamics AX client after deployment, otherwise the assemblies may not get copied.

The assemblies will be deployed to a client as and when are needed. This comes down to the following three situations:

  • When you use IntelliSense
  • When you compile code that uses the assembly
  • When code runs on the client in which a call is made to the assembly

Obviously, you will want to have the assembly on your client as a developer, otherwise you will not be able to use IntelliSense or compile your code.

Consuming the web service

Now that we have created a service reference in our Visual Studio proxy library and deployed it to Microsoft Dynamics AX, we can use the types in the library from Microsoft Dynamics AX.

First attempt

Let's take a look at the following X++ code that consumes the zip code service to retrieve a place name:

static void Consume_GetZipCodePlaceName(Args _args) { DynamicsAxServices.WebServices.ZipCode.USAZipCodeServiceRef .PostalCodeServiceClient postalServiceClient; DynamicsAxServices.WebServices.ZipCode.USAZipCodeServiceRef .PostalCode postalCode; System.Exception Exception; try { // Create a service client proxy postalServiceClient = new DynamicsAxServices .WebServices. ZipCode.USAZipCodeServiceRef .PostalCodeServiceClient(); // Use the zipcode to find a place name postalCode = postalServiceClient. GetPostCodeDetailByPostCode("10001"); // 10001 is New York // Use the getAnyTypeForObject to marshal the System.String to an Ax anyType // so that it can be used with info() info(strFmt('%1', CLRInterop:: getAnyTypeForObject(postalCode.get_PlaceName()))); } catch { // Get the .NET Type Exception exception = CLRInterop::getLastException(); // Go through the inner exceptions while(exception) { // Print the exception to the infolog info(CLRInterop:: getAnyTypeForObject(exception.ToString())); // Get the inner exception for more details exception = exception.get_InnerException(); } } }

When we go through the code bit by bit, we can see that a proxy client is created first. Note that this is the managed type that is created by the SvcUtil tool when adding the service reference:

postalServiceClient = new DynamicsAxServices .WebServices.ZipCode.USAZipCodeServiceRef .PostalCodeServiceClient();

After that, using the following code, we immediately invoke the service operation with a zip code:

postalCode = postalServiceClient. GetPostCodeDetailByPostCode("10001"); // 10001 is New York

Then, there is a simple infolog message that shows the place name using the following code:

info(strFmt('%1', CLRInterop:: getAnyTypeForObject(postalCode.get_PlaceName())));

Notice the CLRInterop::getAnyTypeForObject method, which is used to marshal between the .NET type System.String and the X++ anyType type before submitting it to the infolog.

That's it for consuming services. However, we also have some exception handling that handles any .NET exceptions while invoking the external service, as shown in the following code snippet:

catch { // Get the .NET Type Exception exception = CLRInterop::getLastException(); // Go through the inner exceptions while(exception) { // Print the exception to the infolog info(CLRInterop:: getAnyTypeForObject(exception.ToString())); // Get the inner exception for more details exception = exception.get_InnerException(); } }

Fixing configuration issues

Although the preceding code example should suffice, you will get an error message when running it. The error message is shown in the following screenshot:

What is going on here is that the service is trying to look for the endpoint's configuration in the application's configuration file but does not find it. This is because Microsoft Dynamics AX is acting as the host application here (Ax32.exe). Therefore, the service tries to open the Ax32.exe.config file and look for the endpoint configuration.

It is clear that putting the configuration details of every service that we want to consume into the Ax32.exe.config file is a bit impractical and should be avoided. The solution to this issue is using the AifUtil class to create the service client.

Let's change the preceding code so that it uses the AifUtil class to point to the right configuration file and see what happens then. Start off by declaring a new variable of the System.Type type at the top of the job, as shown in the following code:

System.Type type;

Take a look at the following line of code:

postalServiceClient = new DynamicsAxServices .WebServices.ZipCode.USAZipCodeServiceRef .PostalCodeServiceClient();

Replace the preceding code with the following two lines of code that use the variable that you just declared:

type = CLRInterop::getType('DynamicsAxServices.WebServices .ZipCode.USAZipCodeServiceRef.PostalCodeServiceClient'); postalServiceClient = AifUtil::createServiceClient(type);

The first line will resolve the .NET type of the service client and pass it to the AifUtil::createServiceClient method. The AifUtil class will then resolve the right configuration file by looking into the VSAssemblies folder for the assembly that contains the specified type. You can see the code of the AifUtil class's createServiceClient method in the following code snippet:

vsAssembliesPath = xApplication::getVSAssembliesPath(); configFilePath = Microsoft.Dynamics.IntegrationFramework. ServiceReference::GetConfigFilePath(serviceClientType, vsAssembliesPath); serviceClient = Microsoft.Dynamics.IntegrationFramework. ServiceReference::CreateServiceClient(serviceClientType, configFilePath);

When you test these changes, the service should be called correctly and should give you an infolog message that shows New York as the place name's field.

Deploying between environments

Although the previous code consumes the external service just fine, there is another impractical issue going on when you want to deploy the code across environments.

Suppose that you want to have different versions of your service running on development, test, and production systems. Then, you will probably have three different addresses for each environment. However, the issue here is that you only have one address available in the proxy class's library.

To solve this issue, we need to update our X++ code one more time. Start by declaring two new variables that will hold the endpoint and endpoint address:

System.ServiceModel.Description.ServiceEndpoint endPoint; System.ServiceModel.EndpointAddress endPointAddress;

You may have to add a reference to the System.ServiceModel assembly to the AOT. To do that, go to the AOT, right-click on the References node, and then click on Add Reference. Next, select System.ServiceModel in the grid, click on Select, and finally, click on OK.

Then, add the following three lines of code just before the line that invokes the service operation:

endPointAddress = new System.ServiceModel.EndpointAddress (""); endPoint = postalServiceClient.get_Endpoint(); endPoint.set_Address(endPointAddress);

What the preceding code does is that it creates an endpoint address for the service client that is to be used. When the endpoint is created, it replaces the endpoint address that is currently being used by the service client. Note that in the preceding example, the address should be replaced by a parameter that is stored in the system. This way, you can set the endpoint address depending on the parameter value of that environment.

Final result

After all these changes, the code that consumes the services will look as follows:

static void Consume_GetZipCodePlaceNameWithEndPoint(Args _args) { DynamicsAxServices.WebServices.ZipCode.USAZipCodeServiceRef. PostalCodeServiceClient postalServiceClient; DynamicsAxServices.WebServices.ZipCode. USAZipCodeServiceRef .PostalCode postalCode; System.ServiceModel.Description.ServiceEndpoint endPoint; System.ServiceModel.EndpointAddress endPointAddress; System.Exception exception; System.Type type; try { // Get the .NET type of the client proxy type = CLRInterop::getType ('DynamicsAxServices.WebServices.ZipCode. USAZipCodeServiceRef.PostalCodeServiceClient'); // Let AifUtil create the proxy client because ......//it uses the VSAssemblies path for the config file postalServiceClient = AifUtil::createServiceClient(type); // Create an endpoint address; this should be a //parameter stored in the system endPointAddress = new System.ServiceModel.EndpointAddress (""); // Get the WCF endpoint endPoint = postalServiceClient.get_Endpoint(); // Set the endpoint address. endPoint.set_Address(endPointAddress); // Use the zipcode to find a place name postalCode = postalServiceClient. GetPostCodeDetailByPostCode("10001"); // 10001 is New York // Use the getAnyTypeForObject to marshal the //System.String to an Ax anyType // so that it can be used with info() info(strFmt('%1', CLRInterop::getAnyTypeForObject (postalCode.get_PlaceName()))); } catch { // Get the .NET Type Exception exception = CLRInterop::getLastException(); // Go through the inner exceptions while(exception) { // Print the exception to the infolog info(CLRInterop::getAnyTypeForObject(exception.ToString())); // Get the inner exception for more details exception = exception.get_InnerException(); } } }


At first sight, the procedure to consume a service in Microsoft Dynamics AX 2012 might seem a bit complex, but once you've done it, you see how easy it really is. Using Visual Studio, you can take advantage of having control over how you create the reference. You can choose whether you want to use message contracts, reuse data types, and so on.

Support for different deployment options also means that it is easier than ever to use managed code. The assemblies are part of the model store and are deployed when needed, so no manual actions are needed to deploy them.

Resources for Article:

Further resources on this subject:

You've been reading an excerpt of:

Microsoft Dynamics AX 2012 R2 Services

Explore Title