Dynamically Generating an Operation Contract in Orcas using WF

This kicks off a set of posts where I'll be discussing some of the interesting features coming out in Orcas.

I want to focus on this post on the Receive Activity, and a nice little feature in the designer that lets you create a contract on the fly, without having to drop into code and write a decorated interface.  This allows us to divide the world into two approaches:

  • One where I design my contract first, and then start creating a workflow to implement the operations on the contract.
  • Design my workflow first, and have it figure out the contract for me (this is what I will focus on in more detail)

Designing a Contract First

This is what most WCF folks will be familiar with:

 [ServiceContract]
public interface IOrderProcessing
{

    [OperationContract]
    bool SubmitOrder(Order order);

    [OperationContract]
    Order[] GetOrders(int customerId);
}

When I drop a receive activity onto a workflow, I can now import this contract:

This will bring up a type chooser that lets me pick my service contract:

This imports all of the details and we can see the the operation picker

If we look at the activity properties we now see the parameters to the method.  The (ReturnValue) is the object that we need to return in the operation.  The order parameter is the message that is going to be passed when the method is called.  I can take that and bind that to values in my workflow or whatever I want to do with it.

 

Designing a Workflow First

The other approach we can take is to create the contract as we create the workflow.  That's right, we don't need to create the contract explicitly in code.  To do that, drop a receive activity onto the designer and double click.  Instead of selecting "Import Contract" select "Add Contract".  This will create a new contract with a basic operation.  By selecting the contract or the operation we can name it something a little nicer.

By selecting the operation, I can customize all of its behavior.  I can create parameters to be passed in, I can set the types of those parameters (as well as the return type of the operation). 

It's relevent to point out that I can select any type that would be valid in a WCF contract.  The drop down list displays the basic types, but by selecting "Browse Type" I am brought into a type picker where I can select custom types.  As you can see below I have created a "CancelOrder" operation that takes in an order, the reason and who authorized the cancellation.

When I click ok, my activity has had new dependency properties added to it, as can be seen in the property grid for the activity.

 

So what's happening here?

In the workflow I created, I used a code separation workflow, so I have an .xoml file which contains the workflow definition.  Let's take a quick peek at how the receive activity is defined (note, some of the xml is truncated, if you view in an rss reader or copy and past you can see all the details, I'll work on updating the blog layout):

     <ns0:ReceiveActivity x:Name="receiveActivity2">
        <ns0:ReceiveActivity.ServiceOperationInfo>
            <ns0:OperationInfo PrincipalPermissionRole="administrators" Name="CancelOrder" ContractName="MyContract">
                <ns0:OperationInfo.Parameters>
                    <ns0:OperationParameterInfo Attributes="Out, Retval" ParameterType="{x:Type p9:Boolean}" Name="(ReturnValue)" Position="-1" xmlns:p9="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                    <ns0:OperationParameterInfo Attributes="In" ParameterType="{x:Type p9:Order}" Name="order" Position="0" xmlns:p9="clr-namespace:WcfServiceLibrary1;Assembly=WcfServiceLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
                    <ns0:OperationParameterInfo Attributes="In" ParameterType="{x:Type p9:String}" Name="reason" Position="1" xmlns:p9="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                    <ns0:OperationParameterInfo Attributes="In" ParameterType="{x:Type p9:String}" Name="authorizedBy" Position="2" xmlns:p9="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                </ns0:OperationInfo.Parameters>
            </ns0:OperationInfo>
        </ns0:ReceiveActivity.ServiceOperationInfo>
    </ns0:ReceiveActivity>

Here you can see that within the metadata of the activity, we have the definition for the contract and the operation that I defined.  When we spin up a WorkflowServiceHost to host this workflow as a service, the host will inspect the metadata for the workflow, looking for all of the endpoints and will create them.  You can also see within the OperationInfo element that I am able to define the PrincipalPermissionRole defining the role of allowed callers, taking advantage of the static security checks which I will talk about in another post.  So, defined declaratively in the XAML is the contract for the operations.  I didn't need to write the interface or the contract explicitly, I was able to write a workflow, specifiy in the workflow how it will communicate and then let the WorkflowServiceHost figure out the nitty gritty details of how create the endpoints.  The other part here that is important to mention is that the config will play a part determining what the transport channel and other such details will be.  Within the config, when we set up an endpoint we need to specify the contract is "MyContract", or the name that we assigned when we created it.

Summary

We talked about the way that we can implement contracts that already exist in a receive activity, as well as how we can use the designer to actually create our contract while we are designing the workflow.  The WorkflowServiceHost does the heavy lifting here in order to enable this little bit of nifty-ness.