Monday, March 26, 2012

No-Code (XAML) Custom Workflow Activities in CRM 2011

With the integration of WF4 in CRM 2011, custom workflow activities for CRM can be created by writing code (CodeActivity) or alternatively they can be created in the WF4 designer using XAML. This post provides a sample/tutorial on how to author a XAML custom workflow activity for CRM.

One thing to note is that a XAML workflow is just a special case in which the XAML custom activity also happens to be the root activity. There is a sample in the SDK on how to do this, but the disadvantage of a XAML workflow is that it cannot be updated/created from the CRM workflow designer. However, in this post I am exploring how to register a XAML custom activity and then use it from the CRM workflow designer like any other custom activity (CodeActivity).

Scenario:

When an account is deactivated, all the child contacts must also be deactivated and then an email must be sent to the account owner.



Solution using XAML Custom Workflow Activity


1. Create a generic “RetrieveMultiple” CodeActivity

This is a custom activity that is simply an implementation of RetrieveMultiple in workflow. It takes as input parameter a string (the fetchXml) and it returns an EntityCollection (the entities retrieved matching the fetchXml):

public sealed class RetrieveMultiple : CodeActivity
{
    [RequiredArgument]
    public InArgument<string> FetchXml { get; set; }

    public OutArgument<EntityCollection> EntityCollection { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        IOrganizationServiceFactory osf = context.GetExtension<IOrganizationServiceFactory>();
        IOrganizationService service = osf.CreateOrganizationService(Guid.Empty);

        FetchExpression fe = new FetchExpression(FetchXml.Get(context));
        EntityCollection.Set(context, service.RetrieveMultiple(fe));
    }
}

Note that this activity has an OutArgument type which is not supported in the CRM workflow designer (EntityCollection), but that is OK because we will not use this activity from the CRM workflow designer.


2. Create the XAML Custom activity

On your VS project (it can be the same as the project for the CodeActivity above) you need to add a new item: Workflow –> Activity. This will add a XAML activity to your project. Opening this activity will open the WF4 designer.

1. Add a reference to Microsoft.Xrm.Sdk.Workflow.dll and Microsoft.Xrm.Sdk.dll to your project.

2. Add the out-of-the-box CRM activities to your toolbox: click Toolbox, under General, right-click and select Choose Items. Click the System.Activities Components tab. Click Browse, and then locate and select the Microsoft.Xrm.Sdk.Workflow.dll file, and then click Open. All Microsoft Dynamics CRM workflow activities will appear in the Choose Toolbox Items dialog box with a check mark next to their name. Click OK. The Microsoft Dynamics CRM workflow activities are now visible in the General tab of the toolbox, and can be used with the designer.

3. Drop a “Sequence” activity in the canvas. The configure some variables as follows:
image


4. Drop a “GerPrimaryEntity” activity inside the sequence. This activity is one of the CRM SDK activities. Then configure the properties as follows:
image
image


5. Drop the “RetrieveMultiple” activity after the GetPrimaryEntity. This corresponds to the code activity we built earlier. If the activity does not appear in the toolbox that’s because you need to build first. Then configure the activity as follows:
image
You need to pass the FetchXml to the activity. In this case, we pass a fetchXML that corresponds to all the child contacts of the parent account:
"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" &
"    <entity name='contact'>" &
"        <attribute name='fullname'/> " &
"        <attribute name='telephone1'/> " &
"       <attribute name='contactid'/>" &
"       <order attribute='fullname' descending='false'/>" &
"       <filter type='and'>" &
"           <condition attribute='parentcustomerid' operator='eq' value='{" + primaryEntity.Id.ToString() + "}'/>" &
"       </filter>" &
"   </entity>" &
"</fetch>"
Note that the value of the parent account is dynamic and it corresponds to the Id of the primary record on which the workflow was triggered.


6. Insert a “ForEach” activity after the previous one and configure it as follows:
image
This activity is a WF4 activity that will loop through the EntityCollection of contacts we have retrieved.


7. Inside the body of the ForEach activity, drop a SetState activity (this activity comes with CRM SDK). This activity will deactivate all the contacts. Configure it as follows:
image


8. Now you are done building your XAML activity. It should look like this:
image


9. Compile your project and register the plugin assembly just like any other plugin assembly. The custom activities will appear on the CRM workflow designer. Note that the RetrieveMultiple CodeActivity should not be valid for the designer because you can only populate the arguments from a XAML activity. Therefore, you can hide this activity from the workflow designer by setting the Name and WorkflowActivityGroupName to <blank> in the Plugin Registration Tool:
image


10. Now you can author your workflow in CRM designer as per the requirements
image



Conclusion

You might argue that it would just be simpler to register one CodeActivity that does everything for you (retrieve the related contacts and deactivate them) or to use cascading, which is probably true. However, this was just a sample to illustrate the power of XAML custom workflow activities. The reason why this is useful is that now that you have your RetrieveMultiple generic activity, then it will be very easy to implement any kind of iteration in a workflow. Additionally, you no longer have the limitation on what datatypes are acceptable as In/OutArguments in custom workflow activities. With XAML custom activities you can have arguments of any type and global variables of any type. You might also be able to create custom activities without writing virtually any code.

On the other hand, the fact that you cannot populate the input/output of XAML workflow activities from the CRM workflow designer is a huge drawback, it would be a really cool future improvement.