Friday, 8 March 2013

CRM 2011 - How to create a Custom Workflow Activity?

When working with Microsoft Dynamics CRM, one of the most common tasks undertaken is to map real life business processes and to automate these within CRM. There are two main methods of creating processes that are triggered based on input or an event.
  1. Plugins are the most popular choice for C# developers who are familiar with Visual Studio and the CRM Sdk. 
  2. Workflows are another useful feature for creating asynchronous processes, which give none-programmers the ability to create simple but powerful components that do not require a software developer. 
The out-of-box Workflow functionality is somewhat limited, you are constrained by the features exposed by the Workflow creation wizard. 

Custom Workflow Activities
Can be used to enhance and provide solutions to functionality you currently cannot achieve solely by a CRM Workflow. Custom WF Activities are a perfect approach to creating reusable pieces of functionality that can be used by both Workflows and Dialog's.

They do however require programming knowledge such as C# .NET.

How to create a Custom Workflow Activity

Background
To help demonstrate the use of Custom Workflow Activities, the scenario is that when a new Contact record is created, you want the Parent Customer Account's marketing opt in/out values to be applied to the contact.

Solution
1. Open Visual Studio 2010 
2. Create a new Project using the Workflow > Activity Library template


3. Delete the .xaml file, as you will not need this
4. Add a new Class to the project, this will be your Custom Workflow


5. Add the following code to your Class
  1. public class MyCustomWFActivity : CodeActivity
  2. {
  3.     public MyCustomWFActivity()
  4.     {
  5.         
  6.     }
  7.  
  8.     [Input("Parent Account")]
  9.     [ReferenceTarget("account")]
  10.     public InArgument<EntityReference> ParentAccount { get; set; }
  11.  
  12.     [Output("Email")]
  13.     [DefaultAttribute("true")]
  14.     public OutArgument<bool> Email { get; set; }
  15.  
  16.     [Output("Phone")]
  17.     [DefaultAttribute("true")]
  18.     public OutArgument<bool> Phone { get; set; }
  19.  
  20.     [Output("Fax")]
  21.     [DefaultAttribute("true")]
  22.     public OutArgument<bool> Fax { get; set; }
  23.  
  24.     [Output("Mail")]
  25.     [DefaultAttribute("true")]
  26.     public OutArgument<bool> Mail { get; set; }
  27.  
  28.     [Output("Bulk Email")]
  29.     [DefaultAttribute("true")]
  30.     public OutArgument<bool> BulkEmail { get; set; }
  31.  
  32.     protected override void Execute(CodeActivityContext context)
  33.     {
  34.         try
  35.         {
  36.             //-----------------------------------
  37.             // Workflow Context & Service Objects
  38.             //-----------------------------------
  39.             IWorkflowContext wfContext =
  40.                 context.GetExtension<IWorkflowContext>();
  41.             IOrganizationServiceFactory serviceFactory =
  42.                 context.GetExtension<IOrganizationServiceFactory>();
  43.             IOrganizationService service =
  44.                 serviceFactory.CreateOrganizationService(wfContext.InitiatingUserId);
  45.  
  46.             //--------------------------
  47.             // Obtain the Input Argument
  48.             // Account EntityReference
  49.             //--------------------------
  50.             var parentAccountRef = ParentAccount.Get<EntityReference>(context);
  51.  
  52.             //---------------------------
  53.             // Retrieve the Account based
  54.             // on the EntityReference Id
  55.             //---------------------------
  56.             Entity account =
  57.                 service.Retrieve(parentAccountRef.LogicalName,
  58.                 parentAccountRef.Id,
  59.                 new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
  60.  
  61.             if (account != null)
  62.             {
  63.                 //---------------------------
  64.                 // Set Output Argument Values
  65.                 //---------------------------
  66.                 this.Email.Set(context, ((bool)account.Attributes["donotemail"]));
  67.                 this.BulkEmail.Set(context, ((bool)account.Attributes["donotbulkemail"]));
  68.                 this.Phone.Set(context, ((bool)account.Attributes["donotphone"]));
  69.                 this.Mail.Set(context, ((bool)account.Attributes["donotpostalmail"]));
  70.                 this.Fax.Set(context, ((bool)account.Attributes["donotfax"]));
  71.             }
  72.         }
  73.         catch (SoapException se)
  74.         {
  75.             //Log Errors
  76.         }
  77.         catch (Exception ex)
  78.         {
  79.             //Log Errors
  80.         }
  81.     }
  82. }

  • Workflow Activity Class will inherit CodeActivity
  • InArguments - define the input parameters that are passed in
  • OutArguments - define output parameters, these are passed back to the calling process
  • Both In and Out Arguments can have default values, a good way to ensure that parameters are not left empty

6. Right click on the Project, select Properties and create a strong name key file. Similar to plugins, custom workflow activities need to be signed.

7. We need to register the assembly, this will be done using the plugin registration tool as follows:



8. Now that we have the assembly registered, we can create a Workflow to consume the functionality we have defined in our custom activity.

9. To create a workflow, follow these steps:



10. We want this workflow to execute on create for the Contact entity, select Contact from the entity drop-down and enter in a process name.



11. First thing we want this workflow to do, is execute our Custom Activity. We have registered the assembly earlier using the Plugin Registration Tool, it should automatically appear in the Add Step drop-down.



12. Click on the Set Properties button



13. Here we need to provide an input value for the InArgument we defined in our Custom Activity. This value needs to be dynamically set at run-time, so we select the Parent Customer field from the Form Assistant.



14. Next we want to add an Update Record step, to update the Contact record



15. Scroll down to the Preferences section and select each of the marketing fields and using the Form Assistant, select the corresponding value for each field.



16. Add a Stop Workflow step, set it to Succeeded.



17. Make sure you have selected the "Record is created" checkbox, this will ensure that the workflow will only execute when a new Contact is created. Then Save and Close.



18. The workflows needs to be activated, select the newly created workflow and click on the Activate button. On the popup window, click on the OK button.



19. Open a Account record, change some of the marketing fields and save the record.



20. Create a new Contact record, and select the previously edited Account record. Now Save and Close.



21. Re-open the newly created Contact and you will notice that the marketing fields on the Contact record now match those from the Parent Account.



That's it, hope you enjoyed the article :)

3 comments:

  1. 5th step how to add the code activity in cs file pls hepl me

    ReplyDelete
    Replies
    1. You have to add the Reference System.Activities and namespace using System.Activities

      Delete

Action Microsoft.Crm.Setup.Common.Analyzer +CollectAction failed. Fatal error during installation

When installing the Srs Data Connection (Microsoft Dynamics CRM Reporting Extensions), you may have experienced the following error: ...