Custom workflow activity to process multiple related records [CRM]

We often need to process multiple records in a workflow, particular those related to the current target record. Think notifying all bookings when an event is rescheduled, or cancelling all orders when a product is cancelled. Unfortunately the OOTB CRM workflow designer does not have for-each or looping steps that would allow us to achieve this.

I have developed a custom workflow activity that would solve this problem in a generic manner. My solution is inspired by (and mostly based on) Lucas Alexander’s solution.

How it works

The custom workflow activity takes the following inputs:

  • A FetchXML query
  • Format parameters to parameterise the FetchXML query
  • A workflow to execute for each record returned by the FetchXML query

1 Workflow Inputs

Essentially the idea is that this step allows us to specify a FetchXML query, then parameterised the query with values from the current record, then triggers a separate workflow for each record returned by the parameterised query.

An example

For example, if a workflow was running on an Account record, and from this we wanted to trigger a separate workflow on all Contacts where the Parent Customer is the current Account record (by name, not ID – more on this later), then we’d define a FetchXML query as below. Notice the {0} placeholder for the condition clause.

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="contact">
    <attribute name="fullname" />
    <attribute name="telephone1" />
    <attribute name="contactid" />
    <order attribute="fullname" descending="false" />
    <link-entity name="account" from="accountid" to="parentcustomerid" alias="ae">
      <filter type="and">
        <condition attribute="name" operator="eq" value="{0}" />
      </filter>
    </link-entity>
  </entity>
</fetch>

In the workflow designer, we’d flatten the above query into a single line of text and use this as the FetchXML Query. For the FetchXML Query Format Argument 1, we specify the name of the current Account record. Finally we specify the separate workflow to execute on the Contacts matching our parameterised query. If we needed additional filtering, then we could use the remaining format arguments (use placeholder {1} in the query for FetchXML Query Format Argument 2, {2} for FetchXML Query Format Argument 3, etc.).

2 Workflow Setup

How about filtering by current record’s ID?

This is possible, but requires a bit more effort on your behalf. This is because the OOTB workflow designer does not have a way for us to get the ID of the current record. You’d need to develop another custom activity to output the ID of the current record, and then use that output as one of the format arguments for the query. A number of people have already developed such custom activities, here is an example.

For the query, of course you can code this by hand, or you can get Advanced Find to generate it for you. The Advanced Find below will perform the filtering by an Account ID. Download this query and replace the value in the condition clause with one of the placeholders.

3 Advanced Find

Show me the code!

Here is the code for the custom activity. As you can see, it is quite straightforward!

namespace BNH.CRM.Activities
{
	using System;
	using System.Activities;
	using Microsoft.Crm.Sdk.Messages;
	using Microsoft.Xrm.Sdk;
	using Microsoft.Xrm.Sdk.Query;
	using Microsoft.Xrm.Sdk.Workflow;

	public sealed class StartWorkflowForRecordsActivity : CodeActivity
	{
		//Inspired by
		//http://alexanderdevelopment.net/post/2013/05/19/scheduling-recurring-dynamics-crm-workflows-with-fetchxml/.

		[RequiredArgument]
		[Input("FetchXML Query")]
		public InArgument<string> FetchXmlQuery { get; set; }

		[Input("FetchXML Query Format Argument 1")]
		public InArgument<string> FetchXmlQueryFormatArg1 { get; set; }

		[Input("FetchXML Query Format Argument 2")]
		public InArgument<string> FetchXmlQueryFormatArg2 { get; set; }

		[Input("FetchXML Query Format Argument 3")]
		public InArgument<string> FetchXmlQueryFormatArg3 { get; set; }

		[Input("FetchXML Query Format Argument 4")]
		public InArgument<string> FetchXmlQueryFormatArg4 { get; set; }

		[Input("FetchXML Query Format Argument 5")]
		public InArgument<string> FetchXmlQueryFormatArg5 { get; set; }

		[RequiredArgument]
		[Input("Workflow to Execute")]
		[ReferenceTarget("workflow")]
		public InArgument<EntityReference> WorkflowToExecute { get; set; }

		protected override void Execute(CodeActivityContext context)
		{
			var serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
			var service = serviceFactory.CreateOrganizationService(Guid.Empty); //Use current user's ID

			var recordsToProcess = service.RetrieveMultiple(new FetchExpression(GetFormattedFetchQuery(context)));
			foreach (var record in recordsToProcess.Entities)
			{
				var workflowRequest = new ExecuteWorkflowRequest
				{
					EntityId = record.Id,
					WorkflowId = this.WorkflowToExecute.Get(context).Id
				};
				service.Execute(workflowRequest);
			}
		}

		private string GetFormattedFetchQuery(CodeActivityContext context)
		{
			var query = this.FetchXmlQuery.Get(context);
			return String.Format(query,
				this.FetchXmlQueryFormatArg1.Get(context),
				this.FetchXmlQueryFormatArg2.Get(context),
				this.FetchXmlQueryFormatArg3.Get(context),
				this.FetchXmlQueryFormatArg4.Get(context),
				this.FetchXmlQueryFormatArg5.Get(context));
		}
	}
}

Download the solution

You can download this custom activity as an unmanaged solution here.

How about CRM Online?

Last but not least, this custom activity should also work on CRM Online. The assembly in the solution above is registered to run in Sandbox mode. If you are going to use this on-prem, consider re-registering it to run without isolation for performance reasons.

Summary

This custom activity allows you to process multiple records as part of your workflow. It is simple, yet powerful and generic. Hopefully this will open up some opportunities for you, and help you avoid writing plugins.

Advertisement

About Bernado

Based in Australia, I am a freelance SharePoint and Dynamics CRM developer. I love developing innovative solutions that address business and everyday problems. Feel free to contact me if you think I can help you with your SharePoint or CRM implementation.
This entry was posted in CRM, Workflow. Bookmark the permalink.

3 Responses to Custom workflow activity to process multiple related records [CRM]

  1. Tony says:

    Hi Bernado,

    I just came across your post. This is cool for updating existing records, but how about creating new ones or sending an email? For example if I have an opportunity that can be approved by a specific group of people stored in a related entity, and I want to: 1) create a record for each of the potential approvers in another entity; 2) generate email to all of the potential approvers. I can have unlimited number of these approvers and as you know CRM won’t allow you to create workflow steps for 1:N relationship. I wish MS would do something about 1:N relationship as it comes to workflows.

  2. Matthias Back says:

    Hi Bernado,

    this is really a cool WF activity! Thanks for sharing.
    In some cases it would be could if mother workflow could send and information the child workflows which he is creating throug the loop.
    For e.g. we have the scenario, that when a lead is assigned to a team, all team members should get an email. The child WF which is sending the email must be on the systemuser entity. and then in this case it is not possible to set inside the email the name of the lead or a hyperlink or the regarding…

    If you can build this, I would be really interested in!

    • Bernado says:

      Hey Matthias,

      It should be possible to enhance this workflow activity to be able to pass information down to the child process. The child process however would need to be an Action instead of a Workflow, as it is not possible to pass parameters when starting a Workflow.

      You would also need to add input parameters to the workflow activity, e.g. Action Parameter 1 Name, Action Parameter 1 Value, etc., so that you can pass them on to the child Action when invoking it. This might get a bit ugly though as you may need to make provision for different parameter types.

      Hope that helps :).

Leave a Reply to Bernado Cancel 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 )

Facebook photo

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

Connecting to %s