Sometimes we need recursive workflows with waiting condition in the system. For example a workflow that fires on
creation and
on change of follow up date of the task. This workflow may wait for few days after “follow up” date and create a new task or send an email. If the follow up date is updated, the system will start a new instance of the workflow. It results in multiple waiting workflows in the system. It will effect the performance of the system.
The ideal situation will be a workflow that can cancel any waiting workflows on start of a new instance of the workflow. The original idea is from Ayaz Ahmed’s blog. I took that idea and came up with this CRM 2011 workflow assembly. It checks for any waiting instances of the workflow and update their status to “Cancelled”. Here is the code.
using System;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Client;
namespace Workflow
{
public class KillWaitingWorkflows: CodeActivity
{
protected override void Execute(CodeActivityContext executionContext)
{
//Get the tracing service
//ITracingService tracingService = executionContext.GetExtension<ITracingService>();
//Get the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//get workflow name from the input property
string sWorkflow = workflowName.Get<string>(executionContext);
//Get a service context to use linq queries and
var ServiceContext = new OrganizationServiceContext(service);
try
{
QueryExpression query = new QueryExpression("asyncoperation");
//get columns
ColumnSet cols = new ColumnSet(new string[] { "asyncoperationid", "statecode" });
//create conditions
ConditionExpression c1 =
new ConditionExpression("name", ConditionOperator.Equal, sWorkflow); // name of the workflow -input property
ConditionExpression c2 =
new ConditionExpression("regardingobjectid", ConditionOperator.Equal, context.PrimaryEntityId);// entity id
ConditionExpression c3 =
new ConditionExpression("statecode", ConditionOperator.Equal, 1);//statecode of waiting
//create the filter
FilterExpression filter = new FilterExpression();
filter.FilterOperator = LogicalOperator.And;
filter.AddCondition(c1);
filter.AddCondition(c2);
filter.AddCondition(c3);
query.ColumnSet = cols;
query.Criteria.AddFilter(filter);
//get the collection of results
EntityCollection colResults = service.RetrieveMultiple(query);
foreach (Entity async in colResults.Entities)
{
Entity e = async;
//change the status of the system job
e["statecode"] = new OptionSetValue(3); //cancelled
//update the object
service.Update(e);
}
}//end try
// Catch any service fault exceptions that Microsoft Dynamics CRM throws.
catch (Exception ex)
{
// You can handle an exception here or pass it back to the calling method.
throw;
}
}
[Input("Work Flow Name")]
[Default("workflow name")]
public InArgument<string> workflowName { get; set; }
}
}
- Compile the workflow assembly and register it using plugin registration tool.
- If everything goes smoothly, you can see this workflow assembly available in workflow. It will look like the following screens.
- I am calling the assembly before the waiting condition. Click on the “View properties” link next to assembly name highlighted in yellow to specify the name of the workflow.
- Activate the workflow and test it.
The problem with this code is that we have to pass “workflow name” as input parameter to the assembly. Then an ideas came to me to use this for my advantage. Wait of my next blog “Workflow assembly to cancel waiting workflows Part 2”.