Skip to main content

Consume an AX7 custom web service by SOAP endpoint

Dynamics 365 for Operations (a.k.a. AX7) provides several endpoints for web service. In this blog post, I want to describe consuming a D365O custom web service in a C# application using the SOAP endpoint.
For a detailed description about service endpoints, you can read the official documentation at https://docs.microsoft.com/en-us/dynamics365/operations/dev-itpro/data-entities/services-home-page.
The main advantage of the SOAP protocol is its descriptive functionality through the WSDL language. SOAP endpoints provide detailed description about contracts and parameters to call each service method. Visual Studio has a great functionality that can read the service description and automatically generate proxy classes to access the service methods.
Let’s do an example of consuming a D365O web service in Visual Studio.
  • Run Visual Studio and go to File – New Project, go to Templates – Visual C# – Windows Classic Desktop on the left side of the window and select Console App on the right side. Enter the name of the project and click OK.
Create Visual Studio C# Console App project
Create Visual Studio C# Console App project
Let’s add a new service reference and generate proxy classes.
  • Right click on Visual Studio project and select Add – Service reference
In opened window, we need to enter URL to WSDL of our service. In this example, I propose to consume the D365O standard financial service so the WSDL URL would be something like…
https://{AOSANAME}.cloudax.dynamics.com/soap/services/financialdimensionservice
Generate proxy classes
Add Service Reference
  • Enter FinancialServices in the Namespace field. Click OK and Visual Studio will generate proxy classes for the financial services endpoint.
You can see the generated classes in Object browser (right click on new node FinancialServices under the Connected services node).
View in Object Browser
View in Object Browser
On this screenshot, you can see automatically generated classes (contracts and client).
Proxy classes and contracts
Proxy classes and contracts
To make web authentification you will need to add some libraries.
  • Right click on Visual Studio project and select Manage NuGet Packages.
Manage NuGet packages
Manage NuGet Packages
  • Type IdentityModel in the search box for searching packages.
Search library
Search library
  • Visual Studio will find the Microsoft library Microsoft.IdentityModel.Clients.ActiveDirectory. Select this library and click Install button on right side of this window. And click Ok to continue.
Let’s try to consume service.
Before consuming the financial service, we need to complete authentication. We will authenticate through web authentication.
Declare some configuration variables:
string aosUri = "https://{AOSNAME}.cloudax.dynamics.com/";
string activeDirectoryTenant = "https://login.windows.net/{COMPANY_DOMAIN}";
string activeDirectoryClientAppId = "11d85570-c352-44e4-b56a-eff677b411f4";
string activeDirectoryClientAppSecret = "hv8zt21Lxq87MwJCFaqP2x/1rzxU1eTQicEMXMBlST6=";
string activeDirectoryResource = "https://{AOSNAME}.cloudax.dynamics.com";
…and here’s the authentication logic:
AuthenticationContext authenticationContext = new AuthenticationContext(activeDirectoryTenant);
string aadClientAppSecret = activeDirectoryClientAppSecret;
ClientCredential creadential = new ClientCredential(activeDirectoryClientAppId, aadClientAppSecret);
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync(activeDirectoryResource, creadential).Result;
string oAuthHeader = authenticationResult.CreateAuthorizationHeader();
string serviceName = "financialdimensionservices";
string soapServiceUriString = GetSoapServiceUriString(serviceName, aosUri);
Now get client binding
EndpointAddress endpointAddress = new EndpointAddress(soapServiceUriString);
Binding binding = GetBinding();
We want to get a list of financial attributes and them values. Create the financial service request.
            EndpointAddress endpointAddress = new EndpointAddress(soapServiceUriString);
            Binding binding = GetBinding();
 
            DimensionServiceClient dimServiceClient = new DimensionServiceClient(binding, endpointAddress);
 
            IClientChannel dimServiceChannel = dimServiceClient.InnerChannel;
 
            using (OperationContextScope dimServiceOperContext = new OperationContextScope(dimServiceChannel))
            {
                HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
                requestMessage.Headers[OAuthHeader] = oAuthHeader;
                OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
 
                CallContext callContext = new CallContext {Company = "USMF"};
 
                // Call getDimensionAll method of dimension service
                IList dimensionContracts = ((DimensionService) dimServiceChannel).getDimensionsAll(new getDimensionsAll(callContext)).result;
 
                foreach (DimensionContract dimensionContract in dimensionContracts)
                {
                    Console.WriteLine(string.Format("Dimension: {0}", dimensionContract.parmDimensionName));
 
                    DimensionValueServiceClient valueServiceClient = new DimensionValueServiceClient(binding, endpointAddress);
                    IClientChannel dimValServiceChannel = valueServiceClient.InnerChannel;
 
                    using (OperationContextScope dimValServiceOperContext = new OperationContextScope(dimValServiceChannel))
                    {
                        HttpRequestMessageProperty dimValRequestMessage = new HttpRequestMessageProperty();
                        dimValRequestMessage.Headers[OAuthHeader] = oAuthHeader;
                        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = dimValRequestMessage;
 
                        CallContext dimValCallContext = new CallContext {Company = "USMF"};
 
                        // Call getDimensionValues method which get list of value by specific attribute
                        IList<DimensionValueContract> dimValues = ((DimensionValueService)dimValServiceChannel).getDimensionValues(new getDimensionValues(dimValCallContext, dimensionContract)).result;
 
                        Console.WriteLine("Values: ");
                        foreach (DimensionValueContract dimValue in dimValues)
                        {
                            Console.Write(dimValue.parmValue + ", ");
                        }
                    }
                }
            }
The CallContext class is used to the pass the company, language, user id and partitional key to the service.
Source code for the method GetBinding
        public static Binding GetBinding()
        {
            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
 
            // Set binding timeout and other configuration settings
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
            binding.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
 
            binding.ReceiveTimeout = TimeSpan.MaxValue;
            binding.SendTimeout = TimeSpan.MaxValue;
            binding.MaxReceivedMessageSize = int.MaxValue;
 
            HttpsTransportBindingElement httpsTransportBindingElement = binding.CreateBindingElements().OfType().FirstOrDefault();
            if (httpsTransportBindingElement != null)
            {
                httpsTransportBindingElement.MaxPendingAccepts = 10000; // Largest posible is 100000, otherwise throws
            }
 
            HttpTransportBindingElement httpTransportBindingElement = binding.CreateBindingElements().OfType().FirstOrDefault();
            if (httpTransportBindingElement != null)
            {
                httpTransportBindingElement.MaxPendingAccepts = 10000; // Largest posible is 100000, otherwise throws
            }
 
            return binding;
        }
Source code for method GetSoapServiceUriString
        public static string GetSoapServiceUriString(string serviceName, string aosUriString)
        {
            string soapServiceUriStringTemplate = "{0}/soap/services/{1}";
            string soapServiceUriString = string.Format(soapServiceUriStringTemplate, aosUriString.TrimEnd('/'), serviceName);
            return soapServiceUriString;
        }
Full source code: D365O_SOAPEndpointExample
NOTE: Once you download this source code, you must change placeholders {AOSNAME} and {COMPANY_DOMAIN} in the source code.
The example of changing placeholders:
{AOSNAME} = usnconeboxax1aos
{COMPANY_DOMAIN} = contoso.com
Also, you will need to configure Azure Active Directory applications. Go in D365 menu System Administration -> Setup -> Azure Active Directory applications.
How to do it, you can read in blog https://community.dynamics.com/ax/b/axforretail/archive/2016/09/26/service-to-service-authentication-in-ax7
Let’s build and run the project.
When you run this project you will see Dimensions and their values.
Dimensions and them values
Dimensions and their values

Comments

Popular posts from this blog

Work Flows in Microsoft Dynamics 365 (From Scratch)

Hi everyone, In this blog, I will tell you some basic steps about how to make a workflow. I had some issues in making a workflow so I made this blog so all of you can understand it. The links that I've mentioned below, use them where I've asked in the steps. Links: (For use) https://dynamicsaxinsight.wordpress.com/2015/02/16/ax-2012-create-custom-workflow/ https://community.dynamics.com/ax/b/alirazatechblog/archive/2016/10/19/customization-in-new-dynamics-ax-aka-ax-7-part-6-custom-workflow Steps: 1- Make Base Enum and add values in it.(The values shoule be proper!!!) 2- Customize a table or make a new table as extension wont work in this case because a method is to be over rided. 3- Add this enum to the fields area of that table. 4- Add a field group in the table, it will be used ahead. Fields to be added are of your choice. 5- Override canSubmit.... method of the table. 6- Make a static method in the table. 7- Details of the step 5 and 6 are in the links. 8-

Customization in Dynamics 365 for operation (Dynamics AX, AX 7) – Custom SSRS based on Report

Prerequisite  of this post  is understanding the AOT in Visual studio and basic programming language like C#. In very simple basic level RDP report three objects are required. Data contract,   Data Contract class  ties with extended data types. This class contain properties with getter setters. At run time these properties act as report parameters. Data Provider Class . This class act as container for report logic.  Here we get Report Query or parameters of Data contract class and perform queries on required tables. This class is extended with framework class. Temperory table Temperory table is used as bridge for data between report designer (DataSet) and Report logic. We populate temporary table with required logic in data provider class.  Report Requirement: Client wants a report where End user select Customer and Report shows, Customer Name, Sale order Number, Item Id, Item group. Sales order Quantity, expected delivery date of Sale order and Unit Sales price. The rep