Custom Workflow Messaging Activities

When designing Workflow Services, we can use the Receive or ReceiveAndSendReply out-of-the-box messaging activities to expose our workflow functionalities as WCF services methods (a.k.a. Operation contracts). These activities are accessible from the Toolbox and we can easily drag them onto the workflow designer to configure them.



While this is a fairly simple task, it may quickly become repetitive for developers if they are using a set of custom activities that we developed that are always required to be exposed as WCF service methods. The good thing is, Windows Workflow Foundation (WF) provides us with the extensibility of wiring the out-of-the-box activities with our custom activities.

To wire-up a custom workflow activity with a ReceiveAndSendReply activity, we can implement the Create method of the IActivityTemplateFactory interface.

///
/// Messaging wrapper activity for Submit.
///

public class SubmitReceiveAndSendReply : IActivityTemplateFactory
{
    public Activity Create(DependencyObject target)
    {
        // Sequence container.
        Sequence sequence = new Sequence();
        sequence.DisplayName = "Submit";

        // Add variables to Sequence.
        // Correlation Handle.
        Variable<CorrelationHandle> handle = new Variable<CorrelationHandle>();
        handle.Name = "_handle";
        sequence.Variables.Add(handle);

        // Create Receive Activity.
        Receive receive = new Receive();
        receive.DisplayName = "Submit Receive";
        receive.Action = "http://tempuri.org/IExpenseWorkflowService/Submit";
        receive.OperationName = "Submit";
        receive.ProtectionLevel = ProtectionLevel.None;
        receive.ServiceContractName = "IExpenseWorkflowService";
        receive.CanCreateInstance = true;

        // Add parameters to Receive
        ReceiveParametersContent parameters = new ReceiveParametersContent();
        parameters.Parameters.Add("expense", new OutArgument<Expense>());
        parameters.Parameters.Add("expenseLog", new OutArgument<ExpenseLog>());
        receive.Content = parameters;

        // Add CorrelationInitializer to Receive
        RequestReplyCorrelationInitializer initializer = 
            new RequestReplyCorrelationInitializer();
        initializer.CorrelationHandle = new InArgument<CorrelationHandle>(handle);
        receive.CorrelationInitializers.Add(initializer);

        // Create Send Activity
        SendReply send = new SendReply();
        send.DisplayName = "Submit Reply";
        send.Action = "http://tempuri.org/IExpenseWorkflowService/SubmitResponse";
        send.Request = receive;

        // Custom Activity
        Submit submit = new Submit();
        submit.DisplayName = "Submit";

        // Add activities to sequence.
        sequence.Activities.Add(receive);
        sequence.Activities.Add(submit);
        sequence.Activities.Add(send);

        return sequence;
    }
} 


Note: I do not use the C# Object Initializer syntax as favoured in most MSDN samples (or workflow developers) because I find it difficult to debug since we can't set a breakpoint to the specific line of code when using the initializer syntax.

The above example shows a SubmitReceiveAndSendReply activity. It starts by creating a Sequence activity as a container and followed by initializing a CorrelationHandle as a variable. If there are other variables, they would be declared here as well.

Next, a Receive activity is created and its parameters are defined. The properties in the Receive activity will determine the characteristics of the WCF service method that exposes the custom activity. The example uses a custom activity that accepts 2 parameters, an Expense and an ExpenseLog object and these arguments are both declared as OutArgument. The service method signature for external service clients to call will look something like the following:

Submit(Expense expense, ExpenseLog expenseLog)

Next, a Correlation Initializer is created which uses the CorrelationHandle declared earlier and added to the Receive activity. It then creates a SendReply activity to return a response to the calling client. Noticed it is wired to the Receive activity as well. If there are no SendReply activity, the execution of the service method will be in asynchronous mode.

The method is finally completed with the declaration of the custom activity and adding all the activities into the sequence container activity in the correct order.

When compiled, the SubmitReceiveAndSendReply activity will appear in the Toolbox. It will look like following when dropped onto a Workflow design surface.


Note: My activity is actually code-generated using LASG, hence, the designer UI.

With this in-place developers who use the custom activity will only need to define the key to CorrelatesOn and specify the workflow variables to bind to.

Tip: The next version of the Vector: WF Activity Generator included in LASG, will include the option to generate the above code ;)

No comments:

Post a Comment

Popular Post