Execute multiple operations with single service call from Client (CRM WebAPI) using Custom Actions.

I’m sure this post will answer 2 questions. a) How to call Custom actions from WebAPI b) How to reduce the “chatting” b/w client n’ server.

When it comes to #b, reducing chatting b/w client n’ server – Imagine you have a custom web resource from which you are creating/updating multiple records in to CRM, In this scenario generally many of us will simply loop through the records and “POST” the entity via WebAPI like this

1
Multiple requests & responses b/w client & server

Just imagine how many service calls we might be doing from client to server overall for all agents in a day and in the life time of the application? Huuggeee.. isn’t it ??   How it would be if we can convert all these per agent/per transaction requests in to one “custom action” some thing like

Execute Custom Action from CRM WebAPI
Execute Custom Action

You can see a clear advantage in terms of reduced service calls b/w client – server and so the obvious performance improvement.

Btw, I found the above illustrations as part of my training happend couple of weeks back and thought it would be nice to explain using the same. Thanks to David who is my trainer 🙂

So, In this post we will see how we can achieve this. As part of this process, we will be first creating a Custom Action which can accept the set of Entities (EntityCollection), A Plugin on top of this Custom Action which actually fires ExecuteMultiple Request and Finally, a WebAPI call to this Custom action from Client side scripting.

Step 1: Create the Process

Let’s create a new Custom action which will take EntityCollection as Input parameters with the following details (For those who are new to Actions:- you have to navigate to Settings >> Process >> New Process to create new action )

Field Value Remarks
Process Name Execute Multiple Operations Any meaningful name
Category Action
Entity None None because we are not tying it up with any Entity
Type New Blank Process

Note that, we have set the Entity to None. With this the action will become unbound action meaning we can execute the action irrespective of entity.

3
Create Custom Action

Create new Input argument as shown below.

Field Value Remarks
Name Entities Any meaningful name
Type EntityCollection
Entity
Required TRUE
Description Some description Not mandatory, but if you are placing any – please go through this bug once.
4
Add EntityCollection input parameter to Action

Nothing else to do in the action. Just Save it and “Activate” the Action.

Step 2: Create Plugin on top of the Action

Note: You must be following different patterns for creating plugins in your project. But in my case, I’m just going through the Dynamics CRM Plugin Project for the sake of simplicity.

  1. Create new Plugin project as shown below

    CRM Plugin Base Class Template
    New Plugin Project from CRM Plugin Base Class Template
  2. Build the project first w/o any change in the code, which will automatically restores all the required libraries from Nuget package.
  3. After successful build, replace the Override ExecuteCrmPlugin with the following code.
     protected override void ExecuteCrmPlugin(LocalPluginContext localContext)
    {
    if (localContext == null)
    {
    throw new ArgumentNullException("localContext");
    }
    
    // Get the input parameters from the Plugin Execution Context
    var entitiesToCreate = localContext.PluginExecutionContext.InputParameters["Entities"] as EntityCollection;
    
    // Ensure the Entities collection is not null, If so - Throw Exception.
    if (entitiesToCreate == null)
    throw new ArgumentNullException("Entities",new Exception("Entities Collection not found to create"));
    
    // Initiate ExecuteMultiple Request to create all records in one call.
    ExecuteMultipleRequest emRequest = new ExecuteMultipleRequest()
    {
    // Not interested to create any record even if One failed - This is my choice. You can decide how you want to go ahead 🙂
    Settings = new ExecuteMultipleSettings() { ContinueOnError = false, ReturnResponses = false },
    Requests = new OrganizationRequestCollection()
    };
    
    // Loop through all the Input Entities and create new CreateRequest object.
    foreach (var ent in entitiesToCreate.Entities)
    {
    CreateRequest createRequest = new CreateRequest() { Target = ent };
    
    // Push the CreateRequest object in to ExecuteMultipleRequest Requests paramter.
    emRequest.Requests.Add(createRequest);
    }
    
    
    // Execute the request using Organization service's Execute Method
    localContext.OrganizationService.Execute(emRequest);
    
    
    
    }

    Notice how we are grabbing the Entity collection from Inputparameters collection of PluginExecutionContext. Once after getting Entities, loop through them and add them to the ExecuteMultipleRequest object to execute with in a single service call.

  4. Build the project and Sign the assembly (this is mandatory step for me as I’m going to use this plugin in my Online instance, It may not be mandatory for you if you are using OnPrem). Click on the Project Properties >> Signing >> Sing the Assembly >> New

    6
    Sign the Plugin Assembly.
  5. Rebuild the project once and register the plugin assembly using Plugin Registration tool.

    Register new Plugin
    Register new Plugin
  6. Register the new step with following information (You can leave the all other options as it is).
    Field Value Remarks
    Message new_ExecuteMultipleOperations Or whatever the name you have choosed in Step1
    Primary Entity None
    Secondary Entity None
    Event PipeLine Post It is fine even if you want to go ahead with Pre execution, there is no difference of it as we don’t have any steps registered in the action it self
    Execution Mode Synchronous
    Register plugin step against Custom action
    Register step against Custom action

    That’s it we are done with Plugin as well. The left out part is the Web resource to call the action by using WebAPI from Javascript.

Step3: WebAPI call to the custom action from Javascript

Note: As an example, I’m showing how we can create 10 “Task” records against a account. I have created it as helper method so that we can use it wherever we want (ie in HTML Web resource, as Ribbon button method etc).

  1. Create a new java script web resource as a Helper file as shown below

    JS Helper Web resource
    JS Helper Web resource
  2. Paste the following Javascript code in to Webresource
    // Get the Client URL
    var clientUrl = Xrm.Page.context.getClientUrl();
    var CONST_API_VER = "/api/data/v8.2/";
    
    
    //Create new Tasks
    function CreateTasks() {
    
    // Get new Sample data.
    var data = SampleData();
    
    var customActionName = "new_ExecuteMultipleOperations";
    
    // Prepare Service URL.
    var serviceUrl = clientUrl + CONST_API_VER + customActionName;
    
    // Prepare Request object which accepts JSON data and returns JSON information.
    var req = new XMLHttpRequest();
    req.open("POST", serviceUrl, false);
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.send(JSON.stringify(data));
    
    // Check the request status and show the error message if any.
    if (req.status == 204) {
    alert("Created tasks !!")
    } else {
    var error = JSON.parse(req.response).error;
    alert(error.message);
    }
    
    
    }
    
    
    // Create Sample data for 10 tasks, which associate with "a" account.
    function SampleData()
    {
    var data = {};
    var collection = [];
    for (var i = 0; i < 10; i++) {
    var ent = {};
    ent["@odata.type"] = "Microsoft.Dynamics.CRM.task";
    ent["subject"] = "test " + i;
    ent["regardingobjectid_account@odata.bind"] = "/accounts(475B158C-541C-E511-80D3-3863BB347BA8)";
    collection.push(ent);
    }
    data.Entities = collection;
    
    return data;
    }

Notice how we have called the Custom Action in CreateTasks method. This is how we should be calling the “Unbound” actions. If the action is bound to an Entity in other words “Bound action”, then we are supposed to use the fully qualified name of that action (which we can see in my next blog post).

ent[“regardingobjectid_account@odata.bind”] = “/accounts(475B158C-541C-E511-80D3-3863BB347BA8)”

Check the usage of Regarding Object Id, If we have to bind the Regading object to a contact then it should be ent[“regardingobjectid_contact@odata.bind”] = “/contacts(<<Contact guid>>)”.

That’s it, we are done with our Web resource as well.

The left out part is, how you are planning to use the Helper method. You can include this helper method as part of some ribbon button as shown here or you can create a nice HTML window and use it as part of your HTML Javascript code.

For the sake of quick test, I just used IE Developer tools and called this CreateTasks method from console and here is the result for you … 🙂

Tasks created against account
Tasks created against account

And the best part of it is, as you can see it is just One service call from Client to Server instead of 10 calls.

Hope this helps in your projects, Share your feedback via comments.

Happy coding !!

7 thoughts on “Execute multiple operations with single service call from Client (CRM WebAPI) using Custom Actions.

  1. Pingback: share – Will

  2. Arunav Dash

    why you have used the plugin.You are creating the task from javascript right.Plugin is only for showing executive multiple request

    Like

  3. Ravi

    Hi Srikanth, In the above example if I were to use POSTMAN and make a client web api call, what would the JSON payload look like? When I tried the below, I get it throws me a bad request with error message “The annotation ‘odata.type’ was found. This annotation is either not recognized or not expected at the current position.”

    “@odata.type”: “Microsoft.Dynamics.CRM.task”,
    “regardingobjectid_account@odata.bind”: “/accounts(ea266e06-0fb5-e711-8117-5065f38b4251)”,
    “subject”: “test-1”}

    Like

    1. Ravi

      {
      “Entities”: [
      {
      “@odata.type”: “Microsoft.Dynamics.CRM.task”,
      “regardingobjectid_account@odata.bind”: “/accounts(ea266e06-0fb5-e711-8117-5065f38b4251)”,
      “subject”: “test-1”
      },
      {
      “@odata.type”: “Microsoft.Dynamics.CRM.task”,
      “regardingobjectid_account@odata.bind”: “/accounts(ea266e06-0fb5-e711-8117-5065f38b4251)”,
      “subject”: “test-2”
      },
      {
      “@odata.type”: “Microsoft.Dynamics.CRM.task”,
      “regardingobjectid_account@odata.bind”: “/accounts(ea266e06-0fb5-e711-8117-5065f38b4251)”,
      “subject”: “test-3”
      }
      ]
      }

      The above tested successfully for me. Thank you, Ravi.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s