Tuesday, October 30, 2012

Workflow assembly to cancel waiting workflows - Part 1

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.
image

  • 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.
image

  • 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”.

5 comments:

  1. Eagerly waiting for Part 2 release :)

    ReplyDelete
  2. Thanks Amreek, I can see that I'll be using this assembly in every Dynamics CRM project.

    ReplyDelete
  3. Why to you create an instance of OrganizationServiceContext "ServiceContext" if you don't use it?

    ReplyDelete