Wednesday, July 22, 2015

HowTo: Avoid simultaneous execution of 2 instances of same workflow for a record

Let’s imagine that you (as a consultant) use waiting workflows (booo, I know that waiting workflows are evil). But let’s imagine and you have a field on a record that is responsible for datetime till when workflow has to wait to proceed. Obviously that change of a datetime field should lead to change of workflow “waiting to proceed” time. But you can’t apply this change to workflow that is triggered already. So to complete the task you have to kill existing instance of a workflow and run new. This article describes custom workflow activity that does the trick.

IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

//Retrieve processid of current workflow instance
Entity asyncOperation = service.Retrieve("asyncoperation",
context.OperationId, new Microsoft.Xrm.Sdk.Query.ColumnSet("workflowactivationid"));

//Querying of waiting instances of workflow for current record except current one
QueryExpression asyncOperationsQuery = new QueryExpression("asyncoperation")
{
ColumnSet = new ColumnSet(false)
};
asyncOperationsQuery.Criteria.AddCondition("asyncoperationid", ConditionOperator.NotEqual, asyncOperation.Id);
asyncOperationsQuery.Criteria.AddCondition("workflowactivationid", ConditionOperator.Equal,
asyncOperation.GetAttributeValue<EntityReference>("workflowactivationid").Id);
asyncOperationsQuery.Criteria.AddCondition("statecode", ConditionOperator.Equal, 1);
asyncOperationsQuery.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, context.PrimaryEntityId);

Entity[] existingInstances = service.RetrieveMultiple(asyncOperationsQuery).Entities.ToArray();

//Canelling
foreach (Entity existingInstance in existingInstances)
{
existingInstance["statecode"] = new OptionSetValue(3);
existingInstance["statuscode"] = new OptionSetValue(32);

service.Update(existingInstance);
}


That’s pretty it. Copy