We are building an integration between the Royal Mail SOAP Shipping API and Microsoft Dynamics AX 2012 R2. In parts 1 , 2 and 3 we have built using Windows Communication Foundation (WCF) some components to handle SOAP elements we need to add to the request message and security headers.
In this part we are going to create a proxy client in X++ and call some of the code we have previously written. We are going to take advantage of X++ ability to use .Net assemblies, including the code we have previously created and deployed to the AOT.
Note: I am going to show the declaration of variables alongside the assignation, getting and setting of the objects, for ease of explanation. However in X++ all variables are declared at the top of the method. The summary of the method at the end of the article will show the proper structure.
We use a CLRObject to call .Net assemblies that are not accessible at compile time, and are accessed at runtime. Our client proxy is one such object.
CLRInterop is a class that provides type marshalling, so that we can use the reference of our proxy RoyalMail.shippingAPIPortTypeClient, get the x++ type and assign this to the CLRObject.
clientType = CLRInterop::getType("RoyalMail.shippingAPIPortTypeClient");
We use the AifUtil class to create the service client and assign this to the service client object. The createServiceClient method looks up the type from the VSAssemblies folder and uses the correct configuration file to get the client proxy configuration.
serviceClient = AifUtil::createServiceClient(clientType);
Now we have a client proxy that works very similar to it does in C#, however there are some differences to overcome, the first is the lack of generics in X++. So we end up with a very verbose way of setting collections. There is none of the property get and setting shorthand available for .Net objects in X++. So where you would be used to assigning to a public Object.Property , in X++ you use the underlying set_Property() and get_Property() methods that used in the complied IL.
As we want the API end point to have a dynamically supplied URL – in case Royal Mail change it for example, or because hard code parameters is a horrible thing to see in code, we need to set the endpoint address.
First of all we are going to create objects in X++ that reference .Net assemblies from the System namespace, because there are not similar methods available in X++.
Then we set a URL, I have used the X++ Parameter pattern here, where a method acts like a C# property, being able to both get and set a variable available publicly. It is usually good practice to use X++ ExtensionTypes, In this case I have created a parameter called RMTTUrl and extended the inbuilt X++ URI type.
This would look something like
public RMTTUrl parmRMTTUrl(RMTTUrl _url = url)
url = _url;
We can then pass a X++ type into a constructor for a .Net object.
uri = new System.Uri(this.parmRMTTUrl());
We then construct an EndPointAddress object. passing the URL we have just created, a DNS Identity with the string “api.royalmail.com” and an AddressHeaderCollection object.
endpointAddress = new System.ServiceModel.EndpointAddress(
Now that we have the EndPointAddress set we can add this to the service client’s endPoint. Unfortunately we have to do this in longhand, assigning to a variable before being able to access its properties.
endPoint = serviceClient.get_Endpoint();
In Part 3 we created a class called EndPointBehaviour which implemented IEndPointBehaviour. This had 2 public methods SetCredentials and ApplyClientBehaviour. The only method we need to call here is SetCredentials to pass in the ClientID and Secret key.
First we reference our EndPointBehaviour class, notice that as we have deployed the c# project to the AOT we have access to its classes and methods.
Then we need to create a reference to an EndPointBehaviourCollection, as generics are not available in X++ we have to use a ClrObject and pass in the strange-looking string as below, to create the collection.
If you compile the c# project and look at the Compiled IL code, you will see that The EndPointBehaviourCollection is complied to “System.Collections.Generic.KeyedByTypeCollection`1[System.ServiceModel.Description.IEndpointBehavior]”.
ClrObject endPointBehavioursCollection =
Again we need to assign to a variable to be able to access the methods and accessors of the object. This time the EndpointBehavioursCollection from the Service Client’s endpoint object to an EndpointBehavioursCollection variable.
We pass across the client ID and Secret key we are given when we set up the Royal Mail account in Part 1.
endPointBehavioursCollection = endPoint.get_EndpointBehaviors();
endPointBehaviour = new RoyalMail.EndPointBehaviour();
Then we need add to the OperationBehaviourCollection. This is slightly different. The OperationBehaviourCollection is several layers down in the Service Clients structure. We need to get a reference to the service endpoint’s contract object and then to its Operations collection.
contract = endpoint.get_Contract();
operations = contract.get_Operations();
To access a collection in X++ we need to make use of the GetEnumerator method that is available to every object that implements .Net’s IEnumerable. This returns an enumerator that we can use to get the OperationDescription
enumerator = operations.GetEnumerator();
Then we move through the collection using the enumerator adding our RoyalMailOperationBehaviour object that we created in part 3, to the collection, so that this will be used by the service client.
operationDescription = enumerator.get_Current();
operationBehaviourCollection = operationDescription.get_Behaviors();
The Full Method
|private ZoomFSRoyalMail.RoyalMail.shippingAPIPortTypeClient getRMServiceClient()|
|ClrObject endPointBehavioursCollection =|
|ClrObject operationBehaviourCollection =|
|throw error (strFmt("Royal Mail API URL is missing for Royal Mail Shipping API"));|
|uri = new System.Uri(this.parmRMTTUrl());|
|endpointAddress = new System.ServiceModel.EndpointAddress(|
|clientType = CLRInterop::getType("RoyalMail.shippingAPIPortTypeClient");|
|serviceClient = AifUtil::createServiceClient(clientType);|
|endPoint = serviceClient.get_Endpoint();|
|endPointBehavioursCollection = endPoint.get_EndpointBehaviors();|
|endPointBehaviour = new RoyalMail.EndPointBehaviour();|
|contract = endpoint.get_Contract();|
|operations = contract.get_Operations();|
|enumerator = operations.GetEnumerator();|
|operationDescription = enumerator.get_Current();|
|operationBehaviourCollection = operationDescription.get_Behaviors();|
We have created our service client that connects the Royal Mail endpoint and added our custom WCF classes created in Parts 1, 2 and 3, to the client.
In the next Post
We will add a the WS Security that Royal Mail require as part of the API request message.