Issues in debugging custom workflow assemblies

Three months ago, I wrote a post about profiling workflows with custom activity step -> Debugging custom workflow assemblies

There were couple of comments in the post, about it not working as expected due to

  1. Different version of CRM and/or
  2. Different version of Plugin Registration tool

I will now give you the steps to get this working.

  1. Install LINQPad -> https://www.linqpad.net
  2. Install CRM Driver for LINQPad -> https://crmlinqpad.codeplex.com/
  3. After creating a connection to your CRM instance, run this LINQ query
    XNamespace mxswa = "{clr-namespace:Microsoft.Xrm.Sdk.Workflow.Activities;assembly=Microsoft.Xrm.Sdk.Workflow, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}";
    var query = from w in WorkflowSet.AsEnumerable()
    where
    w.Name == "[YOUR WORKFLOW NAME WITH CUSTOM STEP]" &&
    w.FormattedValues["type"] == "Definition"
    && w.IsCrmUIWorkflow.GetValueOrDefault()
    orderby w.ModifiedOn descending
    select new {
    w.Id,
    w.Name,
    WorkflowVersion = XElement.Parse(w.Xaml).Attributes().FirstOrDefault(a => a.Name.LocalName == "mxswa").Value.Split(';')[1],
    CustomSteps = from a in XElement.Parse(w.Xaml).Descendants($"{mxswa}ActivityReference")
    			   where !a.Attribute("AssemblyQualifiedName").Value.Contains("Microsoft.Crm")
    			  select new {
    			  CustomStepName = a.Attribute("DisplayName").Value,
    			  AssemblyName = a.Attribute("AssemblyQualifiedName").Value,
    			  HasArguments = a.Descendants($"{mxswa}ActivityReference.Arguments").Any()}
    };
    query.Where(c=>c.CustomSteps.Any()).Dump();
    
  4. You’ll get something like this. Note that the workflow in my case is “Blank Workflow with Custom Step”. Change this to the one you are trying to profile.Query

Issue 1: Cannot see the workflow step.

Blank Step

If you cannot see the workflow step, that means that there is a mismatch between the Microsoft.Xrm.Sdk.Workflow.dll version and/or your custom workflow step version. Go the folder with the Plugin Registration tool and check the version of the Microsoft.Xrm.Sdk.Workflow.dll version. The important thing is if the major version or minor version of the Microsoft.Xrm.Sdk.Workflow.dll assembly in Plugin Registration tool folder, is different from the one in the Workflow XAML, you will not see the step displayed.

CRM 2016 Plugin Reg Tool
CRM 2016 Plugin Registration Tool
CRM 2016 Update 1 Plugin Reg Tool.png
CRM 2016 Update 1 Plugin Registration Tool

Compare this with the results of the LINQ query and confirm that the major version and minor version match. Do this same for your custom workflow assembly as well.

Issue 2: NullReferenceException

There is a bug in the Plugin Registration Tool, that doesn’t allow you to profile workflow steps, that doesn’t have any argument. If you try to do this, you will get this exception.

Null Reference.png

The workaround for this is to add a dummy argument, to keep the plugin profiler happy.

Workflow

Hope this helps with your debugging efforts.

Bug: Entity primary field & realtime workflows

When you create a new entity, one of the fields that is generally left unchanged is the primary field for the entity. It is usually “[publisherprefix]_name”.

image

This field can be either required or left optional. This value of this field, is what shows up in the entity lookups. The value of this field is usually set automatically using JavaScript, Workflow or Plugin. If the value for this field is left null, it manifests a bug in the realtime workflow.

What is the bug?

If the value of the primary field is null on a parent entity, any child entity can’t use “if condition” on the parent relationship field. It would result in a “KeyNotFound” exception.

Replication steps

  1. Create an entity that will be the parent entity
  2. Create an entity that will be the child. Create a relationship to the parent entity image
  3. Make the primary field on the parent entity as optional image
  4. Create a new realtime workflow on the child entity, with a condition statement that refers the parent lookup field in the child entity image
  5. Create a parent record without the name field setimage
  6. Create a child record that has the parent lookup set the record created in the previous step. The parent lookup will be displayed as “no name” as the primary field for the parent entity is not set.image
  7. Execute the realtime workflow against the child record in the previous step. A “The given key was not present in the dictionary” exception will be displayedimage

The issue is happening both in CRM 2015 and CRMOnline on version 8.1.0.362. This issue seems to be only affecting realtime workflows and actions, and not background workflows.

EDIT (24/06/2016): Issue logged on Connect -> https://connect.microsoft.com/site687/feedback/details/2851391/realtime-workflow-action-crash-display-name-if-condition-keynotfoundexception

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

Debugging custom workflow assemblies

Plugin Registration tool can be used to debug not just plugin assemblies, but also custom workflow assemblies. I am not sure how well known this feature is, as I could not find any documentation about this in msdn.

Prerequisite

Use the correct version of Plugin Registration tool for your organisation. I could not use Plugin Registration tool that came along with CRM SDK 7.1.1, to debug a workflow assembly running in CRM 2015 Update 0.1 (7.0.1). I could however use Plugin Registration tool 8.0.0.7198 to debug workflow assembly running in CRMOnline 2016 (8.0.1.79). So, it seems the major version and minor version have to match to enable workflow assembly debugging.

After installing the profile, select the plugin profile node. You will now see a new button called “Profile Workflow

Workflow Profile Button

Next, choose the CRM workflow which contains the custom workflow assembly. If the workflow has multiple custom workflow assembly steps, you will see each of these in this screen. You can choose the assembly to profile. It is best to choose “Persist to Entity” option, as you won’t see the exception when it is thrown by the workflow.Profile Settings

 

Once you click OK, a clone of the workflow will be created. It will have “(Profiled)” in the end. Your original workflow will now be in “Draft” state.

Workflow Cloned

Now execute the profiled workflow manually. Confirm that the workflow has finished running.

Workflow Log

Once the workflow has completed running, you can now use the serialised profile to debug the workflow assembly. After you click the “Replay Plugin Execution” button you will see this screen, which will help you to select the correct profile record.

Debug Profile Selection

I ran the workflow manually only once and hence there is only one profile row. If the workflow ran multiple times, you will see multiple rows. After you click select, you’ll then have to choose the correct workflow assembly that matches this profile.

Start Debug

Now attach the Visual Studio Debugger to the Plugin Registration Tool. Now is also a good time to put couple of breakpoints in the workflow assembly code in Visual Studio. Once you click “Start Execution”, the control should now be transferred to Visual Studio to facilitate debugging.

Visual Studio Debug

The behaviour is exactly same as plugin debugging. You can step though the code, understand the root cause of any weird behaviour and resolve it. Once you complete the execution, you will see the trace logs in the profiler.

Debug Result

One more thing: You can use this exact same process to debug an Action which has a custom assembly. Here is how the profile looks when an action with custom workflow assembly is profiled.

Debug an action

Things to note:

  1. When a workflow step is profiled, the workflow assembly containing this activity is cloned and registered as a profiled workflow assembly. It is this assembly that is used in the workflow that has the name ending with “(Profiled)”
  2. Even though you can see the workflow being profiled, stopping the workflow profile using the Plugin Registration tool seems impossible. You can see what is being profiled.Profiled Workflow
  3. In order to stop profiling, you’ll have to
    • Unpublish and delete the workflow/action that ends with “(Profiled)
    • Delete the weirdly named assembly (all guid name) from the “Default Solution“. This will be under the “Plugin Assemblies” nodeCloned Workflow Assembly

I hope this technique will help you to debug custom workflow assemblies quite easily. Happy debugging.

EDIT (01/07/2016): Please refer Issues in debugging custom workflow assemblies where I address the issues in the comment.