Scheduling workflows in CRM 4.0

I am going to deviate a little from the series on which i have been working. I will get back to that series but before i do that i just want to share with you some thought and step by step guide for scheduling your workflows in CRM 4.0. I have not been able to find much help on it so i thought I will present here the essentials for achieving this task.

I wanted to perform certain tasks on entities periodically based on an event. Unfortunately, CRM 4.0 does not have this functionality for scheduling workflows out of the box. There are various ways to achieve this but i will share with you my approach and the reasons for the selection of this solution. Here is what i wanted to do. Each of my account has a contract with our company. That contract like any other contract expires at a certain date. What we want is to perform a certain set of tasks on each entity whose contract has expired. The tasks were created inside a custom workflow. The solution was reduced to simple scheduling of the written workflow based on contract expiration. As mentioned, CRM 4.0 does not support that, so we have written our windows service.

However, there are other ways of doing this task. Your workflow can remain in waiting stage and wait for an event. The only problem with that approach is that whenever you change something all your workflows will have to be hydrated and dehudrated.

Anyways so firstly i created a windows service. I will not be covering this in my blog but if you need to write your own windows service it’s very easy and you can find articles on net. You can mail me if you still need some help.

This is what happens on the “On start” event of my service.

1. Instantiate CrmService

// Set up authentication to be Active Directory mode
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = authType;
token.OrganizationName = orgName;

// Set up the Crm service. Pick up the url from Registry
service = new CrmService();

//Retrieve the port and Server Url from the Registry.
RegistryKey regkey = Registry.LocalMachine.OpenSubKey(“SOFTWARE\\Microsoft\\MSCRM”);
string _serverUrl = regkey.GetValue(“ServerUrl”).ToString();

service.Url = _serverUrl + “/2007/crmservice.asmx”;
service.CrmAuthenticationTokenValue = token;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
2. Get workflow id that needs to be executed. We will be using this workflow_id to schedule this workflow on the accounts that have expired contract.

This can be done based on the workflow id but that means you will have to hard code the Id. I am retreiving the workflow based on the name. This is how you can achieve this.

workflow _contractRenewalWorkflow = null;

// Create the ColumnSet that indicates the properties to be retrieved.
ColumnSet _columnsWorkflow = new ColumnSet(new string[] { “name”, “workflowid” });

// Create the ConditionExpression for association Name field.
ConditionExpression _conditionAssociationName = new ConditionExpression();

// Set the condition for the retrieval to be based on the workflow name.
_conditionAssociationName.AttributeName = “name”;
_conditionAssociationName.Operator = ConditionOperator.Equal;
_conditionAssociationName.Values = new string[] { “Association contract renewal” };

// Create the FilterExpression.
FilterExpression _filterRetrieveWorkflow = new FilterExpression();

// Set the properties of the filter.
_filterRetrieveWorkflow.FilterOperator = LogicalOperator.And;
_filterRetrieveWorkflow.AddCondition(_conditionAssociationName);

// Create the QueryExpression object.
QueryExpression _queryRetrieveWorkflow = new QueryExpression();

// Set the properties of the QueryExpression object.
_queryRetrieveWorkflow.EntityName = EntityName.workflow.ToString();
_queryRetrieveWorkflow.ColumnSet = _columnsWorkflow;
_queryRetrieveWorkflow.Criteria = _filterRetrieveWorkflow;

// Retrieve the workflow
BusinessEntityCollection workflows = _service.RetrieveMultiple(_queryRetrieveWorkflow);

_contractRenewalWorkflow = (workflow)workflows.BusinessEntities[0];

3. Now i need to fetch all the accounts for which the contract has expired. I will fetch all these accounts and then i will execute workflow on these accounts.

Use retrieve multiple request for this and in case of  custom entities set dynamic entities value.

_requestRetrieveExpiredAssociations = new RetrieveMultipleRequest();RetrieveMultipleResponse _responseRetrieveExpiredAssociations = new RetrieveMultipleResponse();

// set query conditions here based on which you want to retrieve these records.

// Retrieve the entity
_requestRetrieveExpiredAssociations.Query = _queryAssociations;
_requestRetrieveExpiredAssociations.ReturnDynamicEntities = true;
_responseRetrieveExpiredAssociations = (RetrieveMultipleResponse)_service.Execute(_requestRetrieveExpiredAssociations);

4. Loop through each of these accounts and schedule workflow.

// Workflow execute request and response
ExecuteWorkflowRequest _requestExecuteWorkflow = new ExecuteWorkflowRequest();
ExecuteWorkflowResponse _responseExecuteWorkflow = new ExecuteWorkflowResponse();
_requestExecuteWorkflow.WorkflowId = _contractRenewalWorkflow.workflowid.Value;
foreach (DynamicEntity _association in _responseRetrieveExpiredAssociations.BusinessEntityCollection.BusinessEntities)
{
_requestExecuteWorkflow.EntityId = ((Key)_association.Properties[“cc_associationid”]).Value;
_responseExecuteWorkflow = (ExecuteWorkflowResponse)_service.Execute(_requestExecuteWorkflow);
}

That should be it. Based on the time interval set in your windows service, your service will execute check for the accounts that have expired contract. Execute the workflow on each of the acocunts with expired contracts. In my implementation my workflow sends an email to the owner of workflow, creates a ‘contract renewal task’, creates an opportunity for sales team. I will continue with my series of post ‘Crm for dummies’ and write about managing different entities using my scenario of soap factory. Please feel free to put your comments or ask questions.