Friday, December 07, 2012

Distribute Workflow

I am so used to Siebel where I had the possibility to loop through records which satisfy a certain criteria based on the search spec. I have across a requirement where I need to loop through the records based on search spec. So I create a custom workflow activity which will take two input arguments
1. Workflow Name
2. Fetch XML query to filter the target records which the workflow should run

For example, if you want to send a reminder to all those customers who have a meeting tomorrow, simply create the fetch xml using advanced find to list all the appointment activities and create a workflow with base entity as Appointment and use this custom activity to distribute the workflow.

WHAT does this custom activity do?
Simple - It takes in the fetchXML and performs the query. For every record returned it would create a new workflow with primary entity ID as the returned record id.


// <copyright file="DistributeActivity.cs" company="Capgemini">
// Copyright (c) 2012 All Rights Reserved
// </copyright>
// <author>Ganesh Sivakumar</author>
// <date>12/7/2012 2:04:27 PM</date>
// <summary>Implements the DistributeActivity Workflow Activity.</summary>
namespace CRM.Plugins
{
    using System;
    using System.Activities;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;
    using Microsoft.Xrm.Sdk.Query;
    using Microsoft.Xrm.Sdk.Messages;
    using Microsoft.Crm.Sdk.Messages;

    public sealed class DistributeActivity : CodeActivity
    {
        /// <summary>
        /// Executes the workflow activity.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        [Input("Distributed Workflow")]
        [ReferenceTarget("workflow")]
        [RequiredArgument]
        public InArgument<EntityReference> Workflow { get; set; }

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


        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();

            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace("Entered DistributeActivity.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace("DistributeActivity.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId,
                context.InitiatingUserId);

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

            try
            {
                // TODO: Implement your custom Workflow business logic.
                Guid workflowId = Workflow.Get(executionContext).Id;
                string fetchXml = FetchXML.Get<string>(executionContext);
                // Initialize the query with the paging information
                QueryExpression query = PluginHelper.ConvertFetchXMLToQueryExpression(fetchXml, service);
                query.PageInfo = new PagingInfo();
                query.PageInfo.PageNumber = 1;

                RetrieveMultipleRequest req = new RetrieveMultipleRequest()
                {
                    Query = query,
                };

                bool moreRecords = true;
                while (moreRecords)
                {

                    RetrieveMultipleResponse resp = (RetrieveMultipleResponse)service.Execute(req);
                    foreach (Entity e in resp.EntityCollection.Entities)
                    {
                        ExecuteWorkflowRequest workflowRequest = new ExecuteWorkflowRequest();
                        workflowRequest.EntityId = e.Id;
                        workflowRequest.WorkflowId = workflowId;
                        service.Execute(workflowRequest);
                    }

                    // Set the paging information for reading the next page
                    moreRecords = resp.EntityCollection.MoreRecords;
                    query.PageInfo.PagingCookie = resp.EntityCollection.PagingCookie;
                    query.PageInfo.PageNumber++;
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());

                // Handle the exception.
                throw;
            }

            tracingService.Trace("Exiting DistributeActivity.Execute(), Correlation Id: {0}", context.CorrelationId);
        }
    }
}