Gotcha: Optional Action Parameters

Yesterday, when I was looking into some failed tests, I discovered a quirk of action. If you invoke an action and don’t pass in the arguments that are optional, it doesn’t mean this is going to come up as null in the execution context.

Below is the action I used to replicate the behaviour.

Optional

When I execute the action and don’t set any arguments, here is what comes through in the execution context.

Argument Type Value
 Boolean  false
 DateTime  0001-01-01T00:00:00
 Decimal  0
 Entity  null
 EntityReference  null
 Float  0
 Int  0
 Money  null
 Picklist  null
 String  null
 EntityCollection  null

The interesting things to note are:

  1. Action arguments, don’t behave the same as AttributeCollection. When you retrieve an entity using the SDK, get the attribute just using the key (not GetAttributeValue), the key won’t exist in the collection, if the attribute value is null. This is not the same behaviour with the action arguments. The key will always be there in the context, even if was not set during the action invocation.
  2. The default value of action arguments = default value of the primitive type

This essentially means that if you are going to do some processing based on the action argument that is a primitive type (Boolean, DateTime, Decimal, Float, Int, String), don’t make it optional. For e.g. if you make a boolean argument optional, how would you differentiate between an action invocation with the argument set to false, and another one which did not set the argument at all?

tl;dr; For action arguments (both input and output) of type Boolean, DateTime, Decimal, Float, Int and String avoid optional.

Advertisements

Searching by Guid

When a lookup is resolved in CRM in advanced find on entity form, it uses quick find view to resolve the correct record. So, if you search for a value that is present in an attribute that is not a “Find Column” the value will not be resolved to a lookup. In this situation, you will get a red cross icon, indicating that this value has to be manually chosen.

Failed to resolve

Note in the above screenshot that I am searching by Guid of the contact. The primary key of the contact entity is “contactid”, but it cannot be added as a “Quick Find” column.

QuickFind Columns

The default find column of a custom entity will be “[publisher prefix]_name”. This is usually the “Primary field” of the custom entity, unless you have changed it during entity creation. If you just went with the default, this is what it will be. Unlike OOB system entities, which have lot of find columns defined by default, custom entity will have one find column i.e. primary field, defined by default.

In both these scenarios it is not possible to search by the primary key of the record, as indicated by the screenshot above. As a power user, you might know the primary key of the record you exactly want on the lookup. A common scenario that I personally experienced is that, I might create a fetchxml that returns a child result set, and I want to display the parent entity records from advanced find, that have the child lookup set to a particular value.

You could search by name if name is unique or your quickfind columns don’t return multiple results. If you search term returns multiple matches, you will get an yellow exclamation icon indicating that you would have manually resolve it.

Multiple Matches

In this situation where you are searching a contact, it is not uncommon for people to have the exact same name, and if you are searching by name (assuming fullname is a find column) you will end up in this scenario. I present a solution to this problem. It is a custom plugin that fires pre-operation of RetrieveMultiple message and modifies the query to enable searching by primary key.

This is how the plugin is registered.

Plugin

Registration

In this case, I want the primary key search capability only for account and contact, and hence registered steps only for these entities. You could add additional steps similarly for more entities, that you require.

Here is the functionality in action on lookup.

Advanced Find Guid Search

Here is the same behavior on normal saved view search.

View Guid Search

You can find the source code for the plugin at https://github.com/rajyraman/QuickFindManipulate

This a proof-of-concept to give a idea about this capability using RetrieveMultiple. You can extend on this to make the plugin steps configurable and add additional performance optimisations. I hope this will be a useful feature for power users.

Realtime workflow and pre-Operation

Realtime workflow capability is a great addition to Dynamics CRM. It can be used as replacement to plugin, as it exhibits the same synchronous behaviour that executes as a part of the database transaction i.e. any error in the realtime workflow would rollback the transaction.

However in terms are capability, there seems to be two, that are lacking compared to a plugin.

  1. Create – Pre-Operation
  2. Delete – Post-Operation

This can be seen in the UI for realtime workflow. There are two dropdowns that are never editable, when you open a realtime workflow.

Workflow Options

Through this post, I will demonstrate how you can use a realtime workflow to run pre-operation on create and use the workflow context to modify the entity record, before it is passed to the main operation in the pipeline.

Use the below fetchxml, to identify which stage the realtime workflow will run. For create, this will default to “4o” which means it is going to run post operation.

<fetch count="50" >
  <entity name="workflow" >
    <attribute name="triggeroncreate" />
    <attribute name="name" />
    <attribute name="createstage" />
    <filter>
      <condition attribute="mode" operator="eq" value="1" />
      <condition attribute="category" operator="eq" value="0" />
      <condition attribute="type" operator="eq" value="1" />
    </filter>
  </entity>
</fetch>

You then have to update the attribute to “20”, using the CRM SDK. You can use the simple snippet below to do this.

CrmServiceClient crmSvc = new CrmServiceClient(ConfigurationManager.ConnectionStrings["CRMConnectionString"].ConnectionString);

if (crmSvc.IsReady)
{
	var workflowUpdate = new Entity("workflow");
	workflowUpdate.Id = new Guid("[WORKFLOWID]");
	workflowUpdate["createstage"] = new OptionSetValue(20);
	crmSvc.OrganizationServiceProxy.Update(workflowUpdate)
}

This will mean this the workflow is going to run pre-operation of the create message. Now, let see how to modify the entity that is going to be passed on to the pipeline. You would have to write a custom workflow step to do this. In this sample workflow step, I am setting the salutation to “Mr” when the gender is “Male” and to “Ms” when the gender is “Female”.

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;

namespace SampleWorkflow
{
    public class UpdateGenderActivity : CodeActivity
    {
        protected override void Execute(CodeActivityContext executionContext)
        {
            ITracingService tracer = executionContext.GetExtension<ITracingService>();
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            var entity = (Entity)context.InputParameters["Target"];
            if(context.MessageName.Equals("Create", StringComparison.InvariantCultureIgnoreCase) &&
                context.PrimaryEntityName.Equals("contact", StringComparison.InvariantCultureIgnoreCase))
            {
                if(entity.GetAttributeValue
<optionSetValue>("gendercode")?.Value == 1)
                {
                    entity["salutation"] = "Mr";
                }
                else if (entity.GetAttributeValue
<optionSetValue>("gendercode")?.Value == 2)
                {
                    entity["salutation"] = "Ms";
                }
            }
        }
    }
}

So, what it the difference between these two stages. Lets look at the audit trail for the workflow that ran pre-operation on create.

Create Audit

Now lets create a new realtime workflow, that does it in the default post-operation on create. Here is the workflow definition.

Workflow Definition

Here is the audit trail for this workflow, after the record has been created.

Update Audit

As you can see, that contact is first created without “Salutation” and then the realtime workflow runs to update the “Salutation” based on the gender. This is in contrast to the realtime workflow that runs on pre-operation and makes the relevant contact attribute changes, before it is passed on to the main operation.

Quicktip on update pre-operation:

When you select the realtime workflow to run pre-operation, any attributes you access from the target entity in the workflow steps are from the pre-images i.e. these are the old values. The current value is not passed to the realtime workflow. The CRM interface allows you to run the workflow pre-operation for status change, owner change and field change.

Preoperation

References:

  1. Event Execution Pipeline
  2. Create realtime workflows