Dynamics 365This week’s Featured Blog Friday comes from our CTO Daði Árnason, who is based at AGR Dynamics headquarters in Iceland. He details how the development team has worked to integrate AGR 5 with Microsoft’s Dynamics 365.  As always, if you have any questions or comments regarding this blog post, feel free to comment below, tweet us @AGRDynamics, or contact us here.

With the latest release of its flagship ERP system (historically known as AX, but now rebranded as Dynamics 365 for Operations, in this article I will refer to it as AX365) Microsoft decided to move its deployment exclusively to the cloud in the short term, eliminating the on-premise deployment option for its clients.

This provides the third-party software providers (like AGR) with a new set of challenges to be able to integrate with the AX365 systems. AGR solutions have been integrated with various versions of AX quite successfully. What those solutions have in common is that they are done via a database to database connection. This is, however, not possible to do when integrating with AX365.

Since the AX365 deployment is cloud based on a Microsoft controlled environment (called public cloud) AGR needs to communicate with AX365 via OData endpoints or AX custom web services. The solution to this problem can be broken down into 2 phases.

 

AX365 Development

Microsoft introduced the concept of the Common Data Model which aims to be an integration platform between their various cloud solutions such as AX365, CRM and BI.  Data Entities in AX365 are part of that platform. With the data entities comes the possibility to expose endpoints via webservice using the OData protocol. The endpoints can return data via the JSON or XML formats. The project then becomes locating where the relevant data can be accessed in the Common Data Model.

Using the OData protocol requires the third-party providers to connect to the AX instance via Service-to-Service authentication via Azure Active Directory(AAD) and AX 365:

  1. The AAD steps are described in the following link. Once you have completed this step in AAD you will have generated a client id and a client secret, those values will need to be stored for later use.
  2. To configure AX365 to be able to authenticate the requests, a system admin needs to go into the AX system and type “Azure Active Directory Applications” in the search window.  In the following screen, you create a new Azure Active Directory Application by adding a new one, the required fields are:
    1. Client Id: the client Id generated via AAD in the previous step.
    2. Name: the name of the application
    3. User Id: the user in the which the remote application will impersonate once logged in.

Remote Connector implementation.

In order to pull data from the AX365 system via OData, the first step is to get an authentication token from AAD, the following code displays how it can be done using the Active Directory Authentication Library from Microsoft.

public static string GetAdalToken()
{
    string axOAuthTokenUrl = ConfigurationManager.AppSettings["ax_oauth_token_url"];
    string axClientKey = ConfigurationManager.AppSettings["ax_client_key"];
    string axClientSecret = ConfigurationManager.AppSettings["ax_client_secret"];
    string axBaseUrl = ConfigurationManager.AppSettings["ax_base_url"]; 

    UriBuilder uri = new UriBuilder(axOAuthTokenUrl);
 
    AuthenticationContext authenticationContext = new AuthenticationContext(uri.ToString());
    ClientCredential credentials = new ClientCredential(axClientKey, axClientSecret);
 
    AuthenticationResult authResult = authenticationContext	.AcquireTokenAsync(axBaseUrl, credentials).Result;
 
    return authResult.CreateAuthorizationHeader();
}

 

The following constants need to stored in a config file:

  • Ax OAuth Token URL: The url used to get the authentication token from AAD, the usual format is https://login.windows.net/<azure_tenant>/oauth2/token)
  • AX Client id: The client id generated in AAD
  • AX Client Secret: The client secret generated in AAD
  • AX Base Url : The base url of the AX implementation

The next step is then to generate proxy classes for each data entity. AGR uses an addon in Visual Studio to achieve this (OData v4 Client Code Generator, https://github.com/odata/odata.net). Proxies are generated based on the metadata that can be found at the endpoint: http://<ax_base_url>/data. One can use the downloaded metadata to find the name of the endpoint for each data entity.

AGR reads the data via a custom method that allows the for the web call to reused for all endpoints, see below:

 

private static async Task<GenericJsonOdata<T>> CallOdataEndpoint<T>(string requestUri)
{            
    var request =(HttpWebRequest)HttpWebRequest.Create(requestUri);
    request.Accept = "application/json;odata.metadata=none";
    string token = Authenticator.GetAdalToken();
    request.Headers["Authorization"] = token;
    request.Method = "GET";
    var result = new GenericJsonOdata<T>();
    try
    {
        using (var response = (HttpWebResponse)request.GetResponse())
        {
            using (var responseStream = response.GetResponseStream())
            {
                using (var streamReader = new StreamReader(responseStream))
                {
                    var responseString = streamReader.ReadToEnd();
                    result = JsonConvert.DeserializeObject<GenericJsonOdata<T>>(responseString);
                    return result;
                }
            }
        }
    }
    catch(WebException e)
    {
        using (var rStream = e.Response.GetResponseStream())
        {
            using (var reader = new StreamReader(rStream))
            {
                result.Exception = JsonConvert.DeserializeObject<AxBaseException>(reader.ReadToEnd());
                return result;
            }
        }
    }
}

 

The GenericJsonObject is a needed class to be able to convert the JSON data received into a collection of entity classes:

 

public class GenericJsonOdata<T>
{
    public GenericJsonOdata()
    {
        value = new List<T>();
    }
    public List<T> value { get; set;}

    [JsonProperty("@odata.nextLink")]
    public string NextLink { get; set; }

    public AxBaseException Exception { get; set; }
}

 

One thing of interested in the above class is the NextLink property; OData sent over from AX365 limits the amount of entities at 10K. If the limit has been enforced the JSON data will include property for called @odata.nextLink, if the property exists in the JSON then it can be used to loop through the data entities on the AX side.

AGR is proud to present that using the method described above we are able to integrate our flagship product AGR 5 with AX365.

 

 

Facebook Comments
Facebooktwitterredditlinkedin