Skip to main content

Notifications

Announcements

No record found.

Integration With Dynamics 365 For operations using OData proxy generator.

Sukrut Parab Profile Picture Sukrut Parab 71,647 Moderator

In This blog post we are going to look at how to expose and consume data entities in the new OData v4 endpoint in Dynamics 365 for Operations.

We will mainly focus on three task

·       Customize/Use a data entity provided by Microsoft.

·       Register a new application in Azure Active Directory.

·       Implement an OData client to consume OData services, including creating a proxy library for the Microsoft AX OData endpoint.

We are going to create simple Inventory movement journal as an example  Using out of the box available entities  InventoryMovementJournalHeader and InventoryMovementJournalEntry.

Customize Microsoft provided data entity

To make data entity available on Odata endpoint its isPublic property must be set to true. So for any standard entity you must change this property if it’s not already set to yes to make it available  on endpoint. When you create any new entity make sure you set isPublic = true so that its available on oDATA endpoint. In my scenario I am going to use above mentioned entities.

 To validate that configurations are now exposed in OData visit following URL. Here I am using ReleasedProduct entity just to verify URL is working.

<Dynamics AX URL>/data/ ReleasedProducts?cross-company=true

TestEntity.JPG

 

Create a new application in AAD

1.       To create new application you have to log in to Microsoft Azure portal. Once you log in click on Active Directory and then click Applications along the top.

2.       Near the bottom of the browser window, click Add, enter a Name for your application and select the radio button for Native Client Application. Then click the next arrow.

3.       Enter a Redirect URL of “http://MYOData” (don’t specify quotes) and click the tick to complete the wizard.

4.       On the top toolbar, click Configure. Scroll down until you see permissions to other applications. Click Add application.

5.       Select Microsoft Dynamics ERP in the list and click the tick to complete the wizard. The application will be added to the list of applications.

6.       In the Delegate Permissions list, select all the check boxes.

7.       Click the save icon in the toolbar at the bottom of the page, and the application details will be saved.

For more information visit this link

Create ODATA Client

As Visual Studio is unable to directly consume OData v4 services via service references, you will need to use a T4 template which will generate the C# proxy library. More information on this can be found here

Use the OData v4 Client Code Generator in your Visual Studio application to build the OData entity proxy classes. This can be downloaded and installed from with Visual Studio. Once it is installed, you can add the OData client to the application. In the following screenshots, I added it  to my C# Console application project:

OdataV4Generator.JPG

This will create a file with an extension of tt which stands for Text Template. Update the MetadataDocumentUri string value in the tt file so it contains your OData metadata endpoint.

https://******************.dynamics.com/data/$metadata

Once you update URI save this file ,This will read the metadata and build a large class file.It may take several minutes to complete. The generated proxy classes let you instantiate the data entity objects, set properties, and call methods to interact with the OData endpoint.

OdataTT.JPG

Implementing Authentication helper

Create a new C# project and add class classConfiguration.cs.

using System;

namespace AuthenticationUtility
{
    public partial class ClientConfiguration
    {
        public static ClientConfiguration Default { get { return ClientConfiguration.OneBox; } }

        public static ClientConfiguration OneBox = new ClientConfiguration()
        {
            ODataEndpointUri = "https://********dev6358751fc5bd9ac4aos.cloudax.dynamics.com/data",
            
            ActiveDirectoryResource = "https://**********dev6358751fc5bd9ac4aos.cloudax.dynamics.com",
            ActiveDirectoryTenant = "https://login.windows.net/TenatId
            ActiveDirectoryClientAppId = "Your Client Id",
            key = "Your Client Secrete",

    };

        public string ODataEndpointUri { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string ActiveDirectoryResource { get; set; }
        public String ActiveDirectoryTenant { get; set; }
        public String ActiveDirectoryClientAppId { get; set; }

        public string key { get; set; }

    }
}

  1. ODataEndpointUri -The web address for your deployment’s OData endpoint, as per LCS. 
  2. Azure Active directory client ID - The client ID is obtained in step 13 in "Create a web service application in Active Directory".
  3. Azure Active directory client secret - The client secret is obtained in step 13 in "Create a web service application in Active Directory".
  4. Azure Active directory resource - The Azure AD directory resource depicts the Finance and Operations root URL. Note: Do not end this field with a forward slash character (/).
  5. Azure Active directory tenant - The Azure AD directory tenant used with the Finance and Operations server: https://login.windows.net/your-AD-tenant-ID

Create a Dynamics AX OData client

Add another class and name it as OAuthHelper. This class contains whole logic to deal with AAD Authentication. 

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net;

namespace AuthenticationUtility
{
    public class OAuthHelper
    {
        
                
        /// <returns>The authentication header for the Web API call.</returns>
        public static string GetAuthenticationHeader()
        {

            var request = HttpWebRequest.Create(ClientConfiguration.Default.ODataEndpointUri);

            AuthenticationContext authenticationContext = new AuthenticationContext(ClientConfiguration.Default.ActiveDirectoryTenant);
            // Get token object
            var ar = authenticationContext.AcquireToken(ClientConfiguration.Default.ActiveDirectoryResource, new ClientCredential(ClientConfiguration.Default.ActiveDirectoryClientAppId, ClientConfiguration.Default.key));
            // Create and get JWT token
            return ar.CreateAuthorizationHeader();

        }

    }
}


More information about authentication can be found here. Authentication utility project is available on GitHub at this location

Now we will create console application, where in we will use entities InventoryMovementJournalHeader and InventoryMovementJournalEntry to create Movement journal.

public class ODataAXConnector
    {
        private LogMessage logMessageHandler;

        private AXODataContext context;

        public ODataAXConnector(LogMessage logMessageHandler, bool enableCrossCompany)
        {
            this.logMessageHandler = logMessageHandler;

            this.context = new AXODataContext(enableCrossCompany);
        }

        public void CreateJournal(string JournalId, string targetAXLegalEntity)
        {
            // Check if journal exists
            if(!IsJournalExists(JournalId))
            {
                
                InventoryMovementJournalHeader jourHeader = new InventoryMovementJournalHeader();
                DataServiceCollection<InventoryMovementJournalHeader> JournalCollection = new DataServiceCollection<InventoryMovementJournalHeader>(context);
                JournalCollection.Add(jourHeader);
                jourHeader.JournalNumber = JournalId;
                jourHeader.JournalNameId = "IMov";

                                
                    InventoryMovementJournalEntry jourLines = new InventoryMovementJournalEntry();
                    DataServiceCollection<InventoryMovementJournalEntry> JournalneCollection = new DataServiceCollection<InventoryMovementJournalEntry>(context);
                    JournalneCollection.Add(jourLines);

                    

                        jourLines.LineNumber = number;
                        jourLines.JournalNumber = JournalId;
                        jourLines.JournalNameId = "IMov";
                        jourLines.ItemNumber = "1000";
                        jourLines.InventoryQuantity = 1;
                        jourLines.InventorySiteId = "1";
                        jourLines.InventoryWarehouseId = "13";
                        jourLines.OffsetMainAccountIdDisplayValue = "600150"; 
                    
                    context.SaveChanges(SaveChangesOptions.PostOnlySetProperties | SaveChangesOptions.BatchWithSingleChangeset);
                    
                                
            }

            logMessageHandler(string.Format("Created Journal  '{0}' in company '{1}'.", JournalId, targetAXLegalEntity));
           
        }

        private bool IsJournalExists(string JournalId)
        {
            bool exist = false;

            var query = from entity in context.InventoryMovementJournalHeaders
                        where entity.JournalNumber == JournalId
                        select entity;
            if (query.Count() > 0)
                exist = true;
            else
                exist = false;
            return exist;
        }

        
    }

In the above code objects are created for each entity and passed to different  datacollection for journal header and journal lines.  I used the SaveChangesOptions.BatchWithSingleChangeset enum in the SaveChanges method this ensures that if any of the object fails validations during saving data none of the record is created ,so it’s kind of running transaction with in a loop.

  Note the context object used in isJournalExist method (an instance of AXODataContext) provides collections for each data entity which is exposed in the Dynamics AX OData endpoint. You can use regular LINQ style queries on these collections, and the underlying DataServiceContext object handles building the OData JOSN request which in the above example will simply return a response with the resulting count. I.e. because Count() is invoked on the query, the resulting generated JOSN request is an aggregate query; no need to fetch all the data just to count it!

In screenshot below you can see output of the above code

Journal Header:

MovJournHeader.JPG

 

Journal Lines:

JourLines.JPG

 

 Hope this  article helps you in right direction to create integration with Dynamics 365 for operations using data entities.

 

Comments

*This post is locked for comments

  • Community Member Profile Picture Community Member Microsoft Employee
    Posted at

    R_R : Answer to the question is below

    Basically it is to create a new record in system administration module->Azure Active Directory applications form in ax . First Column will be the Client Application ID(You can get from Azure portal) , Name (will be your application registered on Azure) and User Id will be Admin.

  • Community Member Profile Picture Community Member Microsoft Employee
    Posted at

    Hello Sukrut Parab, first I wanted to thank you for a good post.

    And also, wanted to ask you to you explain a bit more.

    In section "Implementing Authentication helper" ->

    2. Azure Active directory client ID

    3. Azure Active directory client secret

    you mention that these values "...obtained in step 13 in "Create a web service application in Active Directory"."

    What is "step 13 in "Create a web service application in Active Directory".""? Where I can find it?

    Thank you in advance.

  • kartikkp7 Profile Picture kartikkp7 365
    Posted at

    Hi, we are not able to generate the proxy file for ODataClient. It givens and error when we run custom tool on ODataclient.tt file.

    System.InvalidCastException: Unable to cast object of type 'Microsoft.OData.Edm.Library.AmbiguousTypeBinding' to type 'Microsoft.OData.Edm.IEdmCollectionType'.