Dynamics 365 connection from .Net Core

Posted by     "Andrey Paltusov" on Monday, July 22, 2019

Contents

Azure Application Registration

   There is a big problem when you plan to work with Dynamics 365 from .NET Core SDK. SDK is not compatible with .NET Core. Alternative possibility to work with Dynamics 365 is OData service. Here is detail guide how to set up connection with sample code.

First you need to register new Application in Azure.

Then assign Dynamics 365 service access

Generate access token.

Don’t forget to copy and save key!

Dynamics User Permission

   Now you need to create Azure Application delegated user in Dynamics 365. First copy Azure Application ID.

Now go to the Dynamics 365 application users and Create new one.

Change form selector to “Application User”.

Input “Application ID” and user information

Save it and don’t forget to assign roles in CRM

Test Console

   Now is time to test OData connection. Code snipped from this resource. To authorize in Azure we need authority endpoint.

Ready to create .Net Core Sample Console

Here is code sample:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace D365.REST
{
    class Program
    {
        //Azure Application / Client ID
        private const string ClientId = "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx";
        //Azure Application Client Key / Secret
        private const string ClientSecret = "xxxxxxxxxxx";

        //Resource / CRM Url
        private const string Resource = "https://xxxxx.crm4.dynamics.com";

        //Guid is your Azure Active Directory Tenant Id
        private const string Authority = "https://login.microsoftonline.com/ xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx /oauth2/token";

        //private static AuthenticationResult _authResult;
        private static string _accessToken;

        static void Main(string[] args)
        {
            var getTokenTask = Task.Run(async () => await GetToken());
            Task.WaitAll(getTokenTask);

            if (getTokenTask.Result == null)
                return;

            //Deserialize the token response to get the access token
            TokenResponse tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(getTokenTask.Result);
            _accessToken = tokenResponse.access_token;

            Task.WaitAll(Task.Run(async () => await DoWork()));
        }

        private static async Task DoWork()
        {
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.BaseAddress = new Uri(Resource);
                httpClient.Timeout = new TimeSpan(0, 2, 0);
                httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
                httpClient.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", _accessToken);

                //Unbound Function
                HttpResponseMessage whoAmIResponse =
                    await httpClient.GetAsync("api/data/v8.2/WhoAmI");
                Guid userId;
                if (whoAmIResponse.IsSuccessStatusCode)
                {
                    JObject jWhoAmIResponse =
                        JObject.Parse(whoAmIResponse.Content.ReadAsStringAsync().Result);
                    userId = (Guid)jWhoAmIResponse["UserId"];
                    Console.WriteLine("WhoAmI " + userId);
                }
                else
                    return;

                //Retrieve 
                HttpResponseMessage retrieveResponse =
                    await httpClient.GetAsync("api/data/v8.2/systemusers(" +
                                              userId + ")?$select=fullname");
                if (retrieveResponse.IsSuccessStatusCode)
                {
                    JObject jRetrieveResponse =
                        JObject.Parse(retrieveResponse.Content.ReadAsStringAsync().Result);
                    string fullname = jRetrieveResponse["fullname"].ToString();
                    Console.WriteLine("Fullname " + fullname);
                }
                else
                    return;

                Console.ReadLine();
            }
        }

        private static async Task<string> GetToken()
        {
            using (HttpClient httpClient = new HttpClient())
            {
                var formContent = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string, string>("resource", Resource),
                    new KeyValuePair<string, string>("client_id", ClientId),
                    new KeyValuePair<string, string>("client_secret", ClientSecret),
                    new KeyValuePair<string, string>("grant_type", "client_credentials")
                });

                HttpResponseMessage response = await httpClient.PostAsync(Authority, formContent);

                return !response.IsSuccessStatusCode ? null
                    : response.Content.ReadAsStringAsync().Result;
            }
        }
    }
}

namespace D365.REST
{
    [DataContract]
    public class TokenResponse
    {
        [DataMember]
        public string token_type { get; set; }
        [DataMember]
        public string expires_in { get; set; }
        [DataMember]
        public string ext_expires_in { get; set; }
        [DataMember]
        public string expires_on { get; set; }
        [DataMember]
        public string not_before { get; set; }
        [DataMember]
        public string resource { get; set; }
        [DataMember]
        public string access_token { get; set; }
    }
}

Profit!

--Andrey Paltusov--


comments powered by Disqus