Sunday, May 31, 2020

How to get entity record Url in Dynamic CRM


You can get a Entity Record Url using getUrl()

executionContext.getFormContext().getUrl();


Here is a generalized method to get entity record url based on Classic or UCI type


getEntityRecordUrl: function (executionContext, forceClassic = false, newWindow = true
{   
    var strUIType = 'forceUCI=1';
    if (forceClassic == true || forceClassic == 1)
        strUIType = 'forceClassic=1';

    if (newWindow == 0)
        newWindow = false;

    var entityName = executionContext.getFormContext().data.entity.getEntityName();
    var entityId = executionContext.getFormContext().data.entity.getId().replace('{', '').replace('}', '');

    var entityRecordUrl = executionContext.getContext().getClientUrl() + '/main.aspx?' + strUIType;
    entityRecordUrl += '&newWindow=' + newWindow;
    entityRecordUrl += '&pagetype=entityrecord&etn=' + entityName + '&id=' +  entityId;

    return entityRecordUrl;
}


Refer here to follow best practice while coding in JavaScript

New way to get security role of a Logged-In user, check here

Saturday, May 30, 2020

no instances found for this user ID in D365 CRM



Note: Do not visit Dynamic 365 Admin Center page anymore, as it seems to be deprecated.


Instead use Power Platform to Create or Manage D365 CRM instance for all information.


Here is a link to Open Power Platform Admin Center : https://admin.powerplatform.microsoft.com


Unfortunately, the Dynamics 365-tab is not showing up at the “Admin centers” section and when you go to the Dynamics 365 management url you get an error. No instances found for this user ID because there is no instance created yet.


To Create a New D365 CRM instance – Click on New Button under Environment and Select YES to create a database for this environment. Follow a simple wizard to create a CRM instance.








Unable to access Microsoft Teams


I assigned Office 365 E3 license to one of the users in my organization and he was not able to access teams. Later after he complains, I observer that on Microsoft Admin portal it is flashing a message when you select the user.

Due to a recent increase in Teams usage, when you assign a Teams license to a user it may take around 24 hours before they’ll be fully set up. Until then, you won't be able to assign Teams policies to them, and they might not have access to some Teams features like calling and audio conferencing.





So, sit tight for few hours until Microsoft deals with the heavy usage license.



Tuesday, May 26, 2020

Retrieve Web API in C# for Dynamic 365 CRM


Please refer to my previous blog here which list out D365 CRM Web API methods in a generic format so we can focus on performing business logic.

Here, I am going to showcase how to call the RETREIVE operation and prepare canonical around it.


Testing the GET Operation

Starting with Crm Lead Entity class (a Crm Data Contract) with the fields declared that is expected as output from D365 WEB Api.

Note: We need NuGet Packages for Assembly Newtonsoft.Json for JsonProperty

public class SingleCrmLeadGet
{
    [JsonProperty("@odata.context")]
    public string odata_context { getset; }

    [JsonProperty("@odata.etag")]
    public string odata_etag { getset; }

    [JsonProperty("subject")]
    public string subject { getset; }

    [JsonProperty("firstname")]
    public string firstname { getset; }

    [JsonProperty("lastname")]
    public string lastname { getset; }

    [JsonProperty("leadid")]
    public string leadid { getset; }

    [JsonProperty("mobilephone")]
    public string mobilephone { getset; }

    [JsonProperty("_ccs_accountmanagerid_value")]
    public string accountmanagerid_value { getset; }

    [JsonProperty("_ccs_accountmanagerid_value@OData.Community.Display.V1.FormattedValue")]
    public string accountmanagerid_name { getset; }

    [JsonProperty("_ccs_contracttypeid_value")]
    public string contracttypeid_value { getset; }

    [JsonProperty("_ccs_contracttypeid_value@OData.Community.Display.V1.FormattedValue")]
    public string contracttypeid_name { getset; }

    [JsonProperty("ccs_rfpid")]
    public string rfpid { getset; }

    [JsonProperty("ccs_rfpissuedate")]
    public string rfpissuedate { getset; }

    [JsonProperty("_parentaccountid_value")]
    public string parentaccountid_value { getset; }

    [JsonProperty("_parentaccountid_value@OData.Community.Display.V1.FormattedValue")]
    public string parentaccountid_name { getset; }
}

Now, we need to write a Business Data Contract that other consumer of our Web API would
get a response and we do not need to scare them with CRM naming conventions.

[DataContract(Name = "GetSingleLeadResponse")]
public class GetSingleLeadResponse
{
    public string LeadId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }
    public string ProjectName { get; set; }
    public string AccountManagerId_Value { get; set; }
    public string AccountManagerId_Name { get; set; }
    public string ContractTypeId_Value { get; set; }
    public string ContractTypeId_Name { get; set; }
    public string RfpId { get; set; }
    public string RfpIssueDate { get; set; }
    public string ParentAccountId_Value { get; set; }
    public string ParentAccountId_Name { get; set; }
}

Method to map Dynamic Crm response to business data contract.

protected GetSingleLeadResponse MapLeadResponse(SingleCrmLeadGet crmLead)
{
    GetSingleLeadResponse leadRes = null;
    if (crmLead != null)
    {
        leadRes = new GetSingleLeadResponse();
        leadRes.LeadId = crmLead.leadid;

        leadRes.FirstName = crmLead.firstname;
        leadRes.LastName = crmLead.lastname;
        leadRes.ProjectName = crmLead.subject;
        leadRes.PhoneNumber = crmLead.mobilephone;

        leadRes.AccountManagerId_Value = crmLead.accountmanagerid_value;
        leadRes.AccountManagerId_Name = crmLead.accountmanagerid_name;

        leadRes.ContractTypeId_Value = crmLead.contracttypeid_value;
        leadRes.ContractTypeId_Name = crmLead.contracttypeid_name;

        leadRes.RfpId = crmLead.rfpid;
        leadRes.RfpIssueDate = crmLead.rfpissuedate.ToString();

        leadRes.ParentAccountId_Value = crmLead.parentaccountid_value;
        leadRes.ParentAccountId_Name = crmLead.parentaccountid_name;
    }
    return leadRes;
}

Finally, here is the calling GetLeadBy - leadId (Guid)

public async Task<GetSingleLeadResponse> GetLeadById(string leadid)
{
    GetSingleLeadResponse businessLeadRes = null;

    string entityUrl = "leads(" + leadid + ")?";
    string selectQuery = "$select=_ccs_accountmanagerid_value,_ccs_contracttypeid_value,ccs_rfpid,
                  ccs_rfpissuedate,_parentaccountid_value,subject,mobilephone,firstname,lastname";

    var result = await GetEntity(entityUrl + selectQuery);

    if (result != null)
    {
        businessLeadRes = MapLeadResponse(JsonConvert.DeserializeObject(result));
    }

    return businessLeadRes;
}


Now our custom Web API controller method.

public class LeadController : ApiController
{
    public async Task<GetSingleLeadResponse> Get(string leadId)
    {

        CrmLeadLogic obj = new CrmLeadLogic();
        var busLeadRes = await obj.GetLeadById(leadId);
        return busLeadRes;

    }
}

POST MAN Response




Monday, May 25, 2020

Generic Web API methods for Dynamic CRM in C#


Here are the generalized methods to Create, Retrieve, Update and Delete entity instances in Dynamic CRM using Web API in C#.

It includes method to get httpclient to access Dynamic 365 CRM instance and access token.


Generic Method Definitions for CRUD Operations


// Note : Please provide your Application Id, Client Secret Key, Organization Url and TennantID
//        These parameter can be parameterized, but for simplicity I kept it like that only.
public static async Task<string> GetAccessToken()
{
    var applicationId = "00000000-0000-0000-0000-000000000000";
    var clientSecret = "client-secrete key";
    var organizationUrl = "https://myOrganization.api.crm8.dynamics.com/";
    var aadInstanceUrl = "https://login.microsoftonline.com";
    var tenantId = "00000000-0000-0000-0000-000000000000";

    var clientcred = new ClientCredential(applicationId, clientSecret);
    var authenticationContext = new AuthenticationContext($"{aadInstanceUrl}/{tenantId}");
    var authenticationResult = await authenticationContext.AcquireTokenAsync(organizationUrl, clientcred);
    return authenticationResult.AccessToken;
}


public static HttpClient GetD365Client()
{
    var accessToken = await GetAccessToken();
    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
    client.DefaultRequestHeaders.Add("OData-Version", "4.0");
    client.DefaultRequestHeaders.Add("Accept", "application/json");
    client.DefaultRequestHeaders.Add("Prefer", "odata.include-annotations=\"*\"");

    return client;
}


// Note : Please provide your Organization Url
public static string GetOrganizationAPIUrl()
{
    return "https://myOrganization.crm8.dynamics.com/api/data/v9.1/";
}


public static async Task<string> GetEntity(string entityQuery)
{
    string resultJson = null; // Output variable
    var httpClient = CrmHelper.GetD365Client();           
    string organizationAPIUrl = CrmHelper.GetOrganizationAPIUrl();
           
    var httpResponse = await httpClient.GetAsync(organizationAPIUrl + entityQuery);

    if (httpResponse.IsSuccessStatusCode)
    {
        if (httpResponse.StatusCode == System.Net.HttpStatusCode.OK)
        {
            resultJson = httpResponse.Content.ReadAsStringAsync().Result;                   
        }
    }
    return resultJson;
}


public static async Task<string> updateEntity(string entityUri, object crmEntiyObj)
{
    string entityId = null; // Output variable
    var httpClient = await CrmHelper.GetD365Client();
    string organizationAPIUrl = CrmHelper.GetOrganizationAPIUrl();

    HttpRequestMessage updateEntityReq = 
              new HttpRequestMessage(new HttpMethod("PATCH"), organizationAPIUrl + entityUri);
    updateEntityReq.Content = 
              new StringContent(JsonConvert.SerializeObject(crmEntiyObj), Encoding.UTF8, "application/json");
    HttpResponseMessage httpResponseResult = 
              httpClient.SendAsync(updateEntityReq, HttpCompletionOption.ResponseContentRead).Result;

    if (httpResponseResult != null)
    {
        IEnumerable<string> uri = null;
        httpResponseResult.Headers.TryGetValues("OData-EntityId", out uri);
        if (uri != null)
        {
            entityId = uri.FirstOrDefault().Replace(organizationAPIUrl, "");                   
        }
    }
    return entityId;
}


public async Task<string> createEntity(string entityName, object crmEntiyObj)
{           
    string entityId = null// Output variable
    var httpClient = await CrmHelper.GetD365Client();
    string organizationAPIUrl = CrmHelper.GetOrganizationAPIUrl();
           
    var content = new StringContent(JsonConvert.SerializeObject(crmEntiyObj), Encoding.UTF8, "application/json");
    var httpResponseResult = httpClient.PostAsync(organizationAPIUrl + entityName, content).Result;

    if (httpResponseResult != null)
    {
        IEnumerable<string> uri = null;
        httpResponseResult.Headers.TryGetValues("OData-EntityId", out uri);
        if (uri != null)
        {
            var txtReplacer = organizationAPIUrl + entityName;
            entityId = uri.FirstOrDefault().Replace(txtReplacer, "").Replace("(", "").Replace(")", "");                   
        }
    }
    return entityId;
}


RETRIEVE operation demo:
Please refer here to know more about making canonical for Crm Entity and sending the response in a user-friendly format which other consumer or business can understand.