Getting Form Context from Sub-grid ribbon button in Dynamics V9.0 Gotchas!

After my last blog on getting the formContext and the client URL from a ribbon button on the form in Dynamics Version 9.0, I was simply taken back by the comments and was truly satisfied knowing that it had helped so many blog readers.

However one of the questions was on how to get the formContext when a sub-grid button was clicked. So basically the requirement here is when the sub-grid button is clicked, the UI elements on the form needs to be accessed like the tabs, fields, sections, formSelector etc

And now with V9.0, since Xrm.Page has been deprecated, we need to use the formContext to access and any form-related values.

So I decided to give this a try.

I created a custom ribbon button on the account sub-grid and then on the click of the function, I added couple of CrmParameters

              < JavaScriptFunction FunctionName="subgridEvent" Library="$webresource:new_contactformload.js">
                <CrmParameter Value="PrimaryControl" />
                < CrmParameter Value="SelectedControl" />



One is PrimaryControl and the other is SelectedControl. Well, PrimaryControl was very helpful last time for a form button, since it gave me the much desired FormContext with which I was able to play with the entire form.

Somehow I was reluctant to use the PrimaryControl still I included it.

SelectedControl is very much necessary because I know it gives me the Grid Context which would help me to play with the Grid. And I was pretty sure that it would give me something to access the FormContext.

All set and good. I clicked on the Test Button in the Account sub-grid.


While debugging, I got my two parameters – PrimaryControl and SelectedControl.

As I expected PrimaryControl was useless and SelectedControl gave me the Grid Context.


I kept on searching for something in the SelectedControl and to  my horror nothing was there to get the FormContext.

I stated thinking otherwise. How about storing the formContext in a global variable when the form loads? Well that does not work either. When you move to associated view, your form refreshes and unfortunately you won’t find that script global variable in there when the sub-grid button is clicked.

So is there no other way? Searched the heck out of google and could not find a permanent solution. Searched in docs. Still no luck.

So back to where I started. No supported way I realized or may be I could not find?

I started digging now into the parameter – PrimaryControl and found a method – get_crmFormControl(). And finally from this I was able to get the formContext.

Code below.

function subgridEvent(primaryControl, selectedControl) {
    var formContext = primaryControl.a.get_crmFormControl().getFormContext();
    var grid = selectedControl.getGrid();

    // interact with the form using formContext now

Please note that this is not a documented approach and hence unsupported. However I could not find anything else from this and no where this scenario is mentioned as well.

Would really like to hear if you get a solution working which is supported.

Hope this helps!

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit or reach out to us at

Firing on-load event every time when record is saved. Exploring the data onload event in Dynamics v9.0

Recently I was working with a customer and came across a requirement where they needed to fire an event on client side every time once the data is saved successfully to process some post-save operations.

As we all know from version CRM 2013 and onwards the formload event does not fire when save is completed. So what is the other way around?

Well there is way to do that. We need to use the to add an event which fires every time when the data is refreshed.

The below code adds the onLoad event of data refresh in the form load event of the account entity.

function formLoad(e) {
    var formContext = e.getFormContext();;

    console.log("form load called.");

function dataOnLoad(e) {
    // write your code here.
    console.log("data onload fired");

formLoad is the event which is registered through Form events. In the formload we are adding the event for data refresh using the method.

The function dataOnLaod fires on the initial page load as well as every time the user clicks on save. And now you have a way to perform your post-save operations in client side whenever the record is saved.

Also please note that the function also fires if function is called.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit or reach out to us at

Getting logged in user roles in client side in Dynamics V9.0

You may be asking? Why this mundane post? After all we have been here close to 8 years since 2011 released and we have millions of time retrieved it using Xrm.Page.context.getUserRoles().

So what’s the fuss in it?

So if you are working on CRM version below 9.0, then it’s no fuss. But if you are working on V9.0 and above, may be this is an interesting read for you.

Xrm.Page is deprecated. So you should no longer be using Xrm.Page.context.getUserRoles()

So what’s the other way. Well you need to do it the V9.0 way. Below is the  code to do the same.

var userRoles = Xrm.Utility.getGlobalContext().userSettings.securityRoles;

And the userRoles variable here contains the list of GUID’s of the roles the user have. Wasn’t that easy?

Also now with the unified interface coming in, you should be very careful about every piece of Javascript you write. There are some properties which works only in UCI and some in Web

For e.g – not only you can get the security roles but also the privilege id’s for all the security roles associated with the user.

var userRolesPrivileges = Xrm.Utility.getGlobalContext().userSettings.securityRolePrivileges;

Unfortunately, the above line only works in UCI and not on the web version.

Debajit Dutta
(Dynamics MVP)
For consultation/ training visit or reach out to us at

{Solved} Call Web API from within a plugin in Dynamics 365 online

*Before I get started with the topic, a quick note on the use cases of this particular post. While everything mentioned in the post would work just fine, this post is more of conceptual and feasibility study than a real life implementation use case. For working with CRM data in same environment, SDK is the best approach. Even for scenarios of consuming CRM data from a different environment from plugin, there are other ways to do the same. This won’t be an ideal approach for enterprise applications*

Now coming to this topic, I came across this question countless number of times. And in a recently conducted training boot camp, I was asked the same question – “Can I invoke WebAPI calls from plugins?”. Also customers having multiple CRM instances in a single tenant and looking to consume data from one environment from plugins in another environment, are looking for an answer to this. To be honest after understanding OAuth and how various types of grants work in Azure Active Directory, I was thinking this should not be a very tough problem.

To my surprise, when I started searching the community I came across many links which suggested it can’t be done and many links which suggested ways but no complete guidance on how to do it.

So decided to challenge myself in it for my blog readers and came out pretty quick within couple of hours. So here we start out journey.

Before we go ahead and discuss on how to do this, let’s understand the problem faced.

The first thing that we need to connect to Web API instance is an access token. And getting an access token now is just piece of cake. You reference Active Directory Authentication Library (you can easily get this from Nuget with the keyword : ADAL) in your project and then use some 4-5 pre-defined lines to get the token.

So what’s the problem with that? The problem is when we try to run the same code in our plugins and try to access the token, we get a security exception. The problem is our online plugins runs in sandbox mode.

So the solution to this would be not to use ADAL. So what are the other options? Off-course we can use plain simple HttpRequest to get the token right? After all internally ADAL might be doing the same right? Not sure about ADAL but certainly this is harder than you think.

The first point to consider here is when the plugin is executing, we do not have the password for the user.

In my blog link – I have explained in detail on how to get the access token without using ADAL. The problem here is, I am  using the password here to get the token. In plugins I will not have that.

So how to tackle this.

If you observe carefully, there are various types of grants in Oauth and one of them “grant_type=password” I have used in the above link. Well since I can’t use that, there is another type called grant_type=client_credentials”. And this is what we are going to use here.

So let’s now jump to the solution.

Step 1:

Register an application of type WebApp/Api in your Azure active directory with proper permission to Dynamics Application and then create an application user in Dynamics with the Application ID of your registered App. Give access to your application user with proper security roles to gain necessary access to entities.

If you are not aware of registering App or the concept of Application User in Dynamics, it’s explained in great detail in the below link

Just set-up the APP and the application user. Don’t try to use the code since it will not work in plugins.

Step 2:

Plugin code

public class WebApiSample : IPlugin
public void Execute(IServiceProvider serviceProvider)
// getting the pipeline context.
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var ent = (Entity)context.InputParameters[“Target”];

            var accessToken = GetTokenWithoutADAL().GetAwaiter().GetResult();
var accountJson = RetrieveAccounts(accessToken).GetAwaiter().GetResult();

            ent[“vvbv_description”] = accountJson;

        private async Task<string> GetTokenWithoutADAL()
string azureAdDomain = “”;
string loginUrl = $”{azureAdDomain}/oauth2/token”;
             string resource = “”;
              string clientId = “9a3590d8-c89e-4de5-bb50-8dfa3d9796a9”;
string clientSecret = “<put your client secret here>”;

            HttpClient client = new HttpClient();
var postData = $”client_id={clientId}&client_secret={clientSecret}&resource={resource}&grant_type=client_credentials”;

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, loginUrl);
request.Content = new StringContent(postData, Encoding.UTF8);
request.Content.Headers.TryAddWithoutValidation(“Content-Type”, $”application/x-www-form-urlencoded”);

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            var responseMessage = await client.SendAsync(request);
var jsonResponseString = await responseMessage.Content.ReadAsStringAsync();

           var jsonContent = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonResponseString);

            return jsonContent[“access_token”];

        private async Task<string> RetrieveAccounts(string token)
string webApiUrl = “″;
             string url = $”{webApiUrl}/accounts?$select=name”;

            HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(“Bearer”, token);

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            HttpResponseMessage response = await client.GetAsync(url);

            string jsonContent = await response.Content.ReadAsStringAsync();

            return jsonContent;

You can do a better job at handling the client_id and client_secret. But this is just sample code and hence I am using this directly.

Also to parse the JSON, I have used the Newtonsoft.json dll which you need to ilmerge with the plugin assembly before uploading to the plugin registration tool. Check for the highlighted code.

And I have registered this plugin on the Pre-create of a test entity. I set the description of the field in this plugin to the account JSON and voila it works!


Eureka! I can see the account JSON in the description field after I create the record.

Isn’t it great guys.

Hope this helps. And hope you just had something interesting to read in CRM world today.

Debajit Dutta

(Dynamics MVP)

For consultation/ training visit or reach out to us at

Xrm.Page is deprecated with Version 9.0. How do I get the Clienturl and form data in Ribbon Actions?

Simple isn’t it? After all it was so easy

All I needed to write to get the ClientUrl

var clientUrl = Xrm.Page.context.getClientUrl();

And to get value of the field on the form

var fieldValue = Xrm.Page.getAttribute(“<fieldname>”).getValue()

So what’s the fuss? Well, if your CRM version is 8.2 and below, no worries. It it perfect.

However with version 9.0 and above, the Xrm.Page has been deprecated. So it means you can use them no longer.

So how do I get the Client Url? The below code does it for you.

var globalContext = Xrm.Utility.getGlobalContext();

var clientUrl = globalContext.getClientUrl();

And how about getting the value of a field on the form?

With version 9.0 and above, you should try to access the form data using the formContext. But again the problem is, to get the formContext, you need to use ExecutionContext. So the next question lies on how to pass executionContext parameter to your Ribbon actions.

For that you need to pass the CrmParameter – Primary control to your ribbon action. Below is the code to get the data using formContext.


function ribbonHandler(fc) {
var formContext = fc;

    var recordId =;
var fieldValue = formContext.getAttribute(“<field_name>”).getValue();


Just another day as a consultant and I hope this blog just adds a grain to your CRM knowledge heap.


Debajit Dutta
(Dynamics MVP)
For consultation/ corporate training visit or reach out to us at

Perform Retrieve using Alternate Key and SDK in Dynamics 365

Alternate keys have been implemented some time back and by now I think most of the consultants working with Microsoft Dynamics are familiar with the concept of alternate keys.

In case you are new to alternate key, you can follow the below documentation here.

Recently I got a request to retrieve using an alternate key. Strangely enough, there are many examples of Update/ Upsert using alternate key but not much with the retrieve using SDK.

Moreover developers coming in new gets confused whenever they try to retrieve using OrganzationService.Retrieve because the method does not have any parameter to take in an alternate key

Well the alternative to that is using the RetrieveRequest. Below is the sample code to retrieve a contact record with alternate key. The alternate key is set up on the Email (emailaddress1) field of the contact entity.

string entName = "contact";
            string alternateKeyfield = "emailaddress1";

            RetrieveRequest retrieveRequest = new RetrieveRequest();
            retrieveRequest.ColumnSet = new ColumnSet(new string[2] { "firstname", "lastname" });
            retrieveRequest.Target = new EntityReference(entName, alternateKeyfield, "");

            RetrieveResponse resp = (RetrieveResponse)proxy.Execute(retrieveRequest);

            if(resp.Entity != null)
                Console.WriteLine($"First Name: {resp.Entity.GetAttributeValue<string>("firstname")}");
                Console.WriteLine($"Last Name: {resp.Entity.GetAttributeValue<string>("lastname")}");

Strangely enough the RetrieveRequest is hardly being used. The highlighted one is the line where we are trying to retrieve using Alternate key which is set on emailaddress1 field.

Please note if there is no record with the specified email address here (, you will get an error. Your code should have appropriate try catch block to handle the same

Hope this helps next time when you try to retrieve a record using alternate key.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit or reach out to us at

{Knowhow} Delete field from managed solution in Target environment when deleted from source environment–Dynamics 365

Dynamics CRM 2016 introduced the concept of solution segmentation, solution patching and cloning and indeed it has come a long way over the last 2-3 years. We are now exposed to CDM, flows, powerapps and what not. But CRM has some old tricks up it’s sleeve which makes you go back to the basics sometimes and I am going to pen down one such scenario here. Even experienced consultants in CRM are sometimes bamboozled by this problem and I hope this is going to benefit my blog readers.

So here is the problem

  • Source Environment (Env X) has a custom field of type Optionset – “new_FieldToBeDeleted” for Entity – Account (named it explicitly for this blog. Don’t follow such convention for stupid names Smile) . The field is Local Optionset.
  • The field is placed on the form of the account.
  • The solution from Env X is exported as managed and deployed to target environment (Env Y).
  • Because of change of requirement, the local optionset now deleted from the source and replace with a global optionset.

The question now arises. How to delete the field from the target environment? It is a managed solution. Hence if you try to delete the field, you will receive an error. Also importing the new managed solution from source won’t help in this case.

Fortunately there is one way to do so with the help of Clone Solution feature.

  • In the solution in the source environment, do all the necessary changes like removing dependencies, deleting the local optionset field, creating a global optionset and creating a new field in Account Entity to map to the global optionset.
  • Select the solution and click on Clone Solution.


  • Put a version number as asked for.
  • Export the solution as managed.
  • While importing the solution in the target environment, check the box – “Stage for Upgrade” and select the option – “Maintain customizations”.


  • Once the solution is imported click on Apply Solution Upgrade button. The field in the target would be deleted as well and you will see the new field in your target environment.


Hope this helps next time you have trouble with your solution management.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit or reach out to us at