Store configuration data using Actions

Many times is necessary to store configuration data in Dynamics CRM. The most commons ways to achieved that is through a custom entity, a XML web-resource or if the configuration data is to be accessed inside a plugin then the secure or insecure parameters of the plugin is also a way to go.

The goal of this post is to outline how to store configuration data through actions. This is obtained through a global action where any output parameter will store the configuration data, as shown in the picture:

action_configuration

Additionally, if the configuration data is to be used in workflows, this approach has the advantage data no code is required to access the configuration data.

 

Pass parameters into a plugin

Let’s suppose we have a Web API making some calls to the Dynamics CRM, in order to retrieve some entity records, and there are plugins running against the Retrieve Multiple message in post-operation stage. The business logic which is implemented in the retrieve multiple plugin should only be executed if the retrieve multiple is made from the Web API, and when is triggered from other sources other than the Web API, the logic should not be executed. This is a challenge I have faced recently.

The first thing I thought, would be, somehow, trying to pass a parameter into the plugin in order to signal when the retrieve multiple is made from the Web API.

The idea I came up with was:

  • Web API: Add a filter condition in the query expression being performed (I was using query expression, the same is valid for Fetch Expression where the custom parameter could be passed in the fetch xml header) which contained the key and its value to pass into the plugin;

retrieve_multiple

  • Pre-operation (Retrieve Multiple): In order to avoid the query expression to fault, due to the condition previously added, it is necessary to remove that condition in the pre-operation stage of the retrieve multiple. Therefore in this stage, the filter condition is accessed, obtained the key and the value, and remove the filter condition from the query expression. As the parameter is needed in the post-operation stage, then it is added to the shared variables;

retrieve_multiple_2

  • Post-operation (Retrieve Multiple): The parameter is obtained from the shared variables.

retrieve_multiple_3

This same principle may be applied to other messages, like create or update where the custom parameters can be passed into the plugin through the target attributes collection (like next figure), being removed in the pre-operation.

retrieve_multiple_4

Client API: Xrm.WebApi

The version 9.0 of Dynamics CRM, now designated by Dynamics 365 Customer Engagemen, has just been released.

The client API object model used with JavaScript webresources brings some new features.

The new namespave Xrm.WebApi provides methods to use Web API to perform CRUD operations and execute Web API actions and functions in Dynamics 365 Customer Engagement. This new feature will simplify a lot the development of JavaScript webresources when execution Web API.

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/clientapi/reference/xrm-webapi

Method Description
createRecord Creates an entity record.
deleteRecord Deletes an entity record.
updateRecord Updates an entity record.
retrieveRecord Retrieves an entity record.
retrieveMultipleRecords Retrieves a collection of entity records.
execute Execute a single action, function, or CRUD operation.
executeMultiple Execute a collection of action, function, or CRUD operations.

 

Custom Workflow Activities: How to access Pre and Post entity images

Dynamics CRM plugins allow in some events to register images which may be pre event or post event images whether they represent a copy of the context entity, before or after the core operation is performed, respectively.

https://msdn.microsoft.com/en-gb/library/gg309673.aspx#bkmk_preandpost

Images are only available for certain events:

http://crmbook.powerobjects.com/extending-crm/plug-in-development-and-workflow-extensions/plug-ins/plug-in-images-pre-vs-post/

Images are registered through the Plugin Registration tool. When registering an image is specified:

  • If is a pre image or post image;
  • Which context entity attributes it contains;
  • The name to get it inside the plugin.

The following image illustrates the registration of a pre image and a post image, for a plugin with a step on the post-operation of the account update, where each image contains only the account Name attribute.

register_images_plugin

The next image shows how to get the registered images inside the plugin.

 

plugin_images

With custom workflows activities there is not the possibility to register images, although the CRM “does it for us”, adding images to the custom workflow activity execution context depending on its stage.

The bellow table outlines all the events and stages where workflows can be executed and which images are available for each stage.

Event Stage

(when the custom workflow activity is executed)

Image available (key )
When record is created Pre-event (*)
Post-event (Sync and Async) PostBusinessEntity
When record:

  • status changes;
  • is assigned;
  • fields change;
Pre-event PreBusinessEntity
Post-event (Sync and Async) PreBusinessEntity

PostBusinessEntity

When record is deleted Pre-event PreBusinessEntity
Post-event (Sync) (*)
Post-event (Async) PreBusinessEntity

(*) (Stage not available to register real-time workflows)

Regarding to images in custom workflow activities:

  • Pre image is accessed through the PreBusinessEntity key;
  • Post image is accessed through the PostBusinessEntity key;
  • Both images contain all the attributes of the context entity.

The next image shows how to get both pre and post images, considering that the custom workflow activity is part of a workflow, running on the update event, asynchronously or synchronously after the entity is updated.

workflow_pre_post_images

Get AliasedValue attributes with Early Bound

Sometimes we need to retrieve data from primary and related entity. Let us consider the following FetchXML to retrieve data of accounts and the related primary contact.

fecthXml

Each entity of the retrieved entity collection contains the attributes of the related entity in its attributes collection, where for each related attribute the:

The next image shows how to access the AliasedValue type attributes. To get any AliasedValue type attribute is required to get them from the base entity, even though, use early bound.

aliasedValue_contact

The AliasedValue class contains the next properties (as shown in the above picture):

  • AttributeLogicalName: contains the name of the attribute on which the select operation was performed;
  • EntityLogicalName: contains the name of the entity the attribute belongs to;
  • Value: contains the value returned by the query.

In order to use early bound with related entities I have developed the flowing extension method to the entity class:

public static T GetRelatedEntity<T>(this Entity entity) where T : Entity, new()
{
   T relatedEntity = new T();
// Get from entity all attribute AliasedValue type which EntityLogicalName is equal to RelatedEntity LogicalName
   entity.Attributes.Where(attr => attr.Value is AliasedValue
&& (attr.Value as AliasedValue).EntityLogicalName.Equals(relatedEntity.LogicalName))
.Select(attr => attr.Value as AliasedValue).ToList().ForEach(attr =>
      {
         relatedEntity.Attributes.Add(attr.AttributeLogicalName, attr.Value);
      });
   return relatedEntity;
}

With this extension method we are able to access the related entities using early bound as shown next, simplifying a lot the way to get attributes of related entities.

extension_method

Custom Actions: how to pass objects in Input and Output parameters

In custom actions it is possible to create input and/or output parameters of all the types displayed on the next picture.

custom_action_in_out_parameters

Despite the available parameters types cover most of all the needs we might face as developers, there may be some situations where it would be great if we could set a parameter as any other type. Well, in fact there is a work-around! The goal of this article is to outline how to achieve that.

Let us suppose that we have a custom action where there is the need to access a reporting server. The next class models an object where the data to access the reporting server may be set and get:

public class ReportServer
{
   public String Url { get; set; }
   public String UserDomain { get; set; }
   public String UserName { get; set; }
   public String UserPassord { get; set; }
}

In the action, instead of creating an input parameter for each ReportServer class property:

  • Create an input parameter named ReportServer string type which will be used to send into the action the ReportServer object serialized as a JSON string;
  • In a Code Activity, inside de action, then the ReportServer object is obtained, de-serializing the JSON string received in the input parameter.

To serialize the object into a JSON string and de-serialize the JSON string back to the object type, the next code may be used:

class JsonHelper
{
   internal static string JsonSerializer<T>(T t)
   {
      string jsonString = null;
      using (MemoryStream ms = new MemoryStream())
      {
         DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
         ser.WriteObject(ms, t);
         jsonString = Encoding.UTF8.GetString(ms.ToArray());
      }
      return jsonString;
   }

   internal static T JsonDeserialize<T>(string jsonString)
   {
      DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
      MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
      T obj = (T)ser.ReadObject(ms);
      return obj;
   }
}

When calling the action, set the input parameter with the ReportServer object as string:

// CALL ACTION
var reportServer = new ReportServer();
var request = new OrganizationRequest("new_Test");
request.Parameters.Add("ReportServer", JsonHelper.JsonSerializer<ReportServer>((reportServer)));
service.Execute(request);

Inside the action (Code Activity) the ReportServer object is rebuilt de-serializing the JSON string.

// Access the Input Parameter String Type Inside the ACTION CodeActivity
var reportServer = JsonHelper.JsonDeserialize<ReportServer>(ReportServer.Get<String>(context));

This approach is obviously valid for any class and for any type of its properties, since the class and its properties can be serialized. The example here described is for input parameters but for output parameters the process is the same.

In custom actions passing any type of object in input and/or output parameters can be achieved through a parameter string type which holds the object in a JSON string.

Web API: entity name to query data

When querying data with Web API it must be specified the metadata Logical Collection Name property of the entity to query.

https://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.metadata.entitymetadata.logicalcollectionname.aspx

For example, the query to retrieve the entity account is like “…/api/data/v8.2/accounts?…” where accounts is the Logical Collection Name of the system entity account.

Most of the cases the Logical Collection Name property corresponds to the plural of the Logical Name property in English grammar.

Although in some situations the Logical Collection Name does not match the English plural of entity Logical Name has described here: http://www.crmanswers.net/2016/02/crm-2016-web-api-and-plural-names.html. In those cases specifying the English plural of the Logical Name leads to the error “Resource not found for the segment”.

The entity Logical Collection Name may be obtained querying the entity metadata or easily through the manged solution ‘Metadata Browser’ (as illustrated on above picture for its_settings custom entity) available on Dynamics CRM SDK.

settings_entity_metadata

XML Web Resource to store configurations

In order to store and access configurations on Dynamics CRM different approaches may be followed:

  • Custom entities to save data settings;
  • Plugin secure or/and unsecure configurations if those settings are to be used on plugin steps;
  • XML web resource (technique shown here).

The example outlined here consists on using a XML web resource to store settings required to access the reporting server web service to render a Dynamics CRM report in pdf format.

The XML web resource is then accessed on a custom action where an email record is created and the report attached to it.

The XML web resource followed the next structure:

<?xml version="1.0" encoding="utf-8" ?>
<ReportServer>
  <Url>http://servername/ReportServer/ReportExecution2005.asmx</Url>
  <UserDomain>domain</UserDomain>
  <UserName>username</UserName>
  <UserPassword>Password</UserPassword>
</ReportServer>

After the web resource is published it may be retrieved. The next code show how to retrieve the web resource and read its content which is stored in base64 format:

String webResourceContent = service.RetrieveMultiple(
    new QueryExpression
    {
        EntityName = "webresource",
        ColumnSet = new ColumnSet("content"),
        NoLock = true,
        Criteria = new FilterExpression
        {
            Conditions =
            {
                new ConditionExpression("name", ConditionOperator.Equal, "XML_WebResourceName")
            }
        }
    }).Entities[0].GetAttributeValue<String>("content");

byte[] binary = Convert.FromBase64String(webResourceContent);
XDocument xmlDocument = XDocument.Parse(System.Text.Encoding.UTF8.GetString(binary));

String url = xmlDocument.XPathSelectElement("//Url").Value;
String userDomain = xmlDocument.XPathSelectElement("//UserDomain").Value;
String userName = xmlDocument.XPathSelectElement("//UserName").Value;
String userPass = xmlDocument.XPathSelectElement("//UserPassword").Value;

Real-Time Workflow vs Plugin: Execution Order

Like plugins, real-time workflows are executed before (pre-operation) or after (post-operation) the core operation. Both are executed inside the same database transaction.

In order to evaluate the behaviour for the execution order when plugins and workflow are execute in the same stage messages, I created the following setup in Dynamics CRM:

  • Created a custom entity named Entity A which contains two text fields named ‘Plugin Text’ and ‘Workflow Text’;
  • Registrated two synchronous plugin steps for Pre Update and Post Update triggered when the Entity A name field is updated:
    • Pre Update Event: sets ‘Plugin Text’ with PLUGIN | PRE UPDATE;
    • Post Update Event: sets ‘Plugin Text’ with PLUGIN | POST UPDATE;
  • Created two real-time workflows triggered when the Entity A name field is updated (one runs before the update and the other runs after the update):
    • Before Update: sets ‘Workflow Text’ with Workflow | PRE UPDATE;
    • After Update: sets ‘Workflow Text’ with Workflow | POST UPDATE;

Next are described the tests I have performed.

Firstly I created and Entity A record, then updated its name to ‘Test update’ and obtained the following audit.

default audit

The audit record shows that:

By default: real-time workflows run before than plugins for both, pre and post, stage events.

This is explained by the default value of the execution order that the Plugin Registration Tool sets for a plugin step, which is equal to one.

pluginRegistrationTool

The execution order defined on Plugin Registration Tool set the Rank property of the sdkmessageprocessingstep entity that specifies the processing order within the stage.

https://msdn.microsoft.com/en-us/library/mt622430.aspx

The workflow entity stores the definition of Real-time workflows. By default the Rank property is not defined (I confirmed this querying the workflow entity through web api). Although there is the possibility of updating it through SDK.

https://msdn.microsoft.com/en-us/library/mt622427.aspx

If the Rank is not defined then these events are executed first. If the Rank is defined then events with lower rank are executed first.

Then I updated the execution order (the Rank property) of the two plugin steps to zero without updating the Rank of workflows. The audit record shows that the plugins steps run before than workflows.

audit rank 0 for plugins

Finally I set the execution order according to the next table:

Event Rank
Real-time workflow Pre Update 0
Plugin Pre Update 0
Real-time workflow Post Update 0
Plugin Post Update 1

audit final

Conclusions:

  • By default real-time workflows are executed before than plugins steps;
  • Plugin steps are executed before than real-time workflows when both have set the same rank.

Retrieve Embedded Images on Reports

Recently I had to build a report where some images were required, although I did not have those images they were available on other reports as embedded images.

From https://msdn.microsoft.com/en-us/library/dd239394.aspx I found which embedded images are stored on reports within its report definition (rdl file) base64 encoded.

So I took the next steps to build the images I needed to add on the new report:

  1. Report definition file stores images on the EmbeddedImages element. So, I copied that section and created an new XML file.
<?xml version="1.0" encoding="utf-8" ?>
<EmbeddedImages>
  <EmbeddedImage Name="logo">
    <MIMEType>image/png</MIMEType>
    <ImageData>image base64 encoded</ImageData>
  </EmbeddedImage>
</EmbeddedImages>
  1. Then created a C# console application with the code bellow to create the image file from the XML file and got the images to add to the report.
XDocument xDoc = new XDocument();
xDoc = XDocument.Load("ReportImages.xml");

foreach (XElement xElem in xDoc.Elements("EmbeddedImages").Elements("EmbeddedImage"))
{
	String name = xElem.Attribute("Name").Value;
	String type = xElem.Element("MIMEType").Value;
	String data = xElem.Element("ImageData").Value;

	String fileName = String.Format("{0}.{1}", name, type.Substring(type.IndexOf('/') + 1));
	File.WriteAllBytes(fileName, Convert.FromBase64String(data));
}

Templates for Data Import: A duplicate column heading exists

The Templates for Data Import feature makes the import data process very simple. The templates generated specify which fields are required, the type of each field and automatically map them to entity fields.

Where are templates accessible?

  • Settings > Data Management (here the user is requested to choose the desired template):

pic1

  • In the ribbon of each entity:

pic2.png

Which fields are available on it?

The template contains:

  • The fields available in any active entity form;
  • All the required fields even though they are not in any entity form;

Each column heading of the template contains the Display Name of every entity field.
The Display Name of each entity attribute is not required to be unique.

Hence, when there are more than one attribute with the same Display Name and fulfilling the criteria to be added to the template, the A duplicate column heading exists error is thrown trying to download the template. To avoid this error all the fields which meet the criteria to be added to the template must have a unique Display Name.