In this article, I would like to talk about actions. Actions are a way to combine several operations in one place and make them available as a single method. In other words, Actions provide the ability to define a single verb (or message) that matches an operation you need to perform for your business. Actions are heavily used by Microsoft to create additional SDK messages which can be later executed from your plugins, workflows or javascript. You can create actions using low code approach as well as using Dynamics CE SDK. Low code approach is quite simple to use, so I would like to focus on creating actions using code.
In case you want to learn more about basics, I would recommend you to read some documentation about this topic: Actions overview. The simplest way to create actions is to use a standard designer. You can define Input and Output parameters and later use this action from your workflows. On the picture below, you can see how does it look like.
In the picture above, you can see that we’ve defined the Input Parameter so that later we can use it in the steps section. When you call this action from another workflow, you will be able to define this parameter.
Let’s now discuss how to create code-based actions. I will focus on the method which Microsoft uses to create custom messages. First of all, let’s talk about the scenario. Let’s assume that we need to update some record, for example, quote product. The quote product should be updated for the closed quote. As you know it is not possible to update quote product for the closed quote if you are not a system administrator. Microsoft has hardcoded this internally in the platform. Our action will always be executed from the system administrator and provide an SDK Message to update the quote.
To implement our custom action, we will do the following three steps :
- Create custom action and define Input and Output parameters.
- Create a plugin which will be executed for our custom action.
- Create javascript to call our custom action.
Let’s start from the simplest part and crate our custom action. On the picture below, you can see how does it look like:
We have three parameters EntityLogicalName, EntityId, and EntityAttributes. I need EntityLogicalName because I also want to implement support for order products. Entity attributes will arrive as a JSON dictionary. We don’t need to add any steps because all the logic will be implemented in the plugin. The json with parameters which is coming to our plugin should look like the following:
The next part is to create a plugin which will serve this request. In our simple scenario, we will only support string attributes, but you can easily extend this code for any types you need.
public class UpdateProductAction: IPlugin { public void Execute(IServiceProvider serviceProvider) { var tracingService = (ITracingService) serviceProvider.GetService(typeof(ITracingService)); var context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext)); if (context.InputParameters.Contains("EntityLogicalName") && context.InputParameters.Contains("EntityId") && context.InputParameters.Contains("EntityAttributes")) { var entityToUpdate = new Entity(); if (context.InputParameters["EntityLogicalName"].ToString().Contains("salesorder")) entityToUpdate.LogicalName = "salesorderdetail"; if (context.InputParameters["EntityLogicalName"].ToString().Contains("quote")) entityToUpdate.LogicalName = "quotedetail"; entityToUpdate.Id = new Guid(context.InputParameters["EntityId"].ToString()); var prop = JsonUtils.ConvertJsonToComplexDictionary(context.InputParameters["EntityAttributes"].ToString()); var serviceFactory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof(IOrganizationServiceFactory)); var service = serviceFactory.CreateOrganizationService(context.UserId); foreach(var key in prop.Keys) { entityToUpdate.Attributes.Add(key, prop[key]); } service.Update(entityToUpdate); } } }
As you see it is a simple plugin which expects some input parameters, do some checks and conversations and as a result, updates the entity.
The next part is to register this plugin for our message is the system.
As you see we’ve just registered our plugin to fire on the execution of the message. Our plugin will always be executed on behalf of the system administrator. We are not pointing any entity so that our message will be a global message.
The next step is to call our message. It is quite easy to call it from C# as you can use SDK method “Execute” with “OrganizationRequest” to execute any custom or standard message you want. Here is an example of how you can call custom action from Java Script:
function updateProduct(entityId, entityLogicalName, properties) { var actionName = "oer_UpdateProductProperties"; var data = { "EntityId": entityId, "EntityLogicalName": entityLogicalName, "EntityAttributes": window.JSON.stringify(properties) }; var req = new XMLHttpRequest(); return new Promise(function(resolve, reject) { req.onreadystatechange = function() { if (req.readyState !== 4) return; if (req.status >= 200 && req.status < 300) { resolve(req); } else { reject({ status: req.status, statusText: req.statusText }); } }; req.open("POST", getClientUrl() + "/api/data/v9.0/" + actionName, true); 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(window.JSON.stringify(data)); }); }
You can also run this custom action from a standard workflow or even using Microsoft Flow.
I hope you got the idea of how to develop code-based actions using Dynamics CE SDK and execute them from different platforms.