Note – this article is based on Workflow 4 Beta 1, some things will change for Beta 2]

First off let me apologise for the length of this post. If you just want the code, skip to the bottom.

I don’t know about you but some words just don’t seem to make their way into my skull very easily, and correlation is one of them. It doesn’t matter how many times I say it in my head, even really slowly  “c-o-r-r-e-l-a-t-i-o-n”, it still makes little sense.

So, in the spirit of sharing I thought I’d post what I know about correlation (in as far as it’s used in Workflow 4), and add in some code to cement the subject. First off though I thought I’d pop upstairs and look at the dictionary definition of correlation so here goes…

  • Correlatehave a relationship or connection in which one thing affects or depends on another
  • Correlationconnection, association, link, tie-in, tie-up, relationship, interrelationship, interdependence, interconnection, interaction

Thanks to the “Concise Oxford English Dictionary” for the former definition, and the “Oxford Paperback Thesaurus” for the latter. I hope they won’t mind me using their definitions here but if they do then someone else wrote this article, it wasn’t me, honest. As an aside, the “Concise OED” is a bit of an oxymoron if you see the physical size of it, but then again if you saw the full OED then you’d realise why this was called the concise version. Anyhow, enough English language for now!

So, how does correlation apply to a workflow then?

In the old days (well, pre Workflow 4), the common way to deal with a workflow instance was to know it’s instance ID which was a GUID. You had a workflow definition (i.e. some XML or a coded workflow), and when you created an instance of that workflow definition you had a workflow instance, and it was uniquely identifiable by its workflow instance ID. Simple.

If I wanted to do anything with a WF 3.x workflow, all I needed to know was it’s GUID and I could load it up, send messages to it, find it in the persistence database and so on. When I created a workflow instance I could optionally choose to assign my own GUID to it rather than having the system generate one for me. Life was good.

Now, lets say you were exposing a workflow as a service. You might have a few operations defined on that service and want to call these in whatever order suited you. You’ll define the first operation (which effectively kicks off the workflow), then subsequent operations will be called on the same instance of the workflow. In traditional programming you would maybe implement an interface something like the following…

[ServiceContract]
public interface IStudent
{
    [OperationContract]
    Guid EnrollStudent(string name, DateTime dob);

    [OperationContract]
    void AddExamResults(Guid studentId, string examName, int mark);

    [OperationContract]
    void Graduate(Guid studentId);
}

So the initial operation is EnrollStudent, and this returns the GUID of that student. Then some time later (days/weeks/months/years) you call AddExamResults and pass through the GUID in order to hook up the results with the right student. Finally at some point in the future the Graduate method is called (OK, maybe they flunked, but I’ll keep this simple). That’s all fairly easy to understand.

Now, if you were doing this in the workflow world things would be a little different. As of now (Workflow 4 Beta 1) you can’t do contract first workflow services, so you need to define the service contract within the workflow itself. Hopefully this limitation will be fixed by the time Beta 2 drops as I quite like contract first development these days.

In the service contract I defined above, the thing that identifies one student from another is the GUID returned from the initial EnrollStudent method. You can conceptually see that this key uniquely defines the student instance, and as long as you use that every time you want to ‘talk’ to that student instance then everything will be just fine.

Moving this service into the world of Workflow 4, we need some identifier that does the same job – it ties my requests to an instance of a workflow. Suppose I have two clients. The first calls EnrollStudent and this constructs an instance of my workflow and then returns the unique ID back to the caller. The second client can call EnrollStudent and will get a different ID and so now there will be two distinct workflow instances ‘running’ (they may be persisted to the database). Now when I get calls to the AddExamResults method I need to have these go to the appropriate workflow instance, and it’s correlation that does that. You don’t have to use GUIDs by the way – correlation in WF 4 allows you to define anything in the message as the correlation ‘handle’ – indeed you can use multiple items in the message as this handle which is very cool indeed. However for this example I’m using just one thing – a GUID.

So, finally, we have my definition of correlation as it applies to WF 4…

  • Correlation – hooks messages to the workflows they are intended to reach

OK, so that’s a fairly poor definition and I don’t think the OED will be knocking on the door any time soon asking me to write for them, but hopefully you get the drift. Enough waffle though, lets get to some code. I’ll try to emulate the above service contract with WF 4, and use code to call that service.

Example – Defining the Workflow

In this example I’ll use code throughout – no XAML here folks! First off I need to define some data contracts for the arguments I’m passing in to the service methods. These mimic the parameters I’m passing to the service contract I outlined above…

[DataContract(Namespace = "http://www.morganskinner.com")]
public class EnrollStudentArgs
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public DateTime DateOfBirth { get; set; }
}

[DataContract(Namespace="http://www.morganskinner.com")]
public class AddExamResultsArgs
{
    [DataMember]
    public Guid StudentId { get; set; }
    [DataMember]
    public string ExamName { get; set; }
    [DataMember]
    public int Mark { get; set; }
}

So here I have two data contracts, one for the EnrollStudent operation and another for the AddExamResults operation. I need to do it this way as there’s no way to define multiple arguments to the Receive activity at present (this will be coming in Beta 2). You would currently need to do this for every method that takes more than a single argument, and whilst it’s a bit of a pain to have to do it that’s just the way it is for now. Well actually that’s a bit of a lie, there is an activity that will do this in Beta 1 but some changes are afoot for Beta 2 so I decided to show you this method which works for now.

Now we need a workflow. For this example I’ve chosen to build the workflow up as follows…

  • An initial call to the EnrollStudent operation is made. This kicks off a new workflow and passes in the EnrollStudentArgs, consisting of the Name and DateOfBirth. This data is stored away within the workflow and a new GUID returned to the caller.
  • The workflow then sits in a loop, waiting for a call to AddExamResults or a call to Graduate.
    • If AddExamResults is called the data is output to the console
    • If Graduate is called then the loop will exit and the workflow will complete

So, the overall workflow will be defined roughly as follows …

image

I’ve simplified it a bit as in the workflow there are actually many more activities, but hopefully this image conveys the main structure. I’ll build the code up step by step and explain what the bits are as I go along.

The first thing you’ll need to define are some variables and the initial Receive activity…

// Correlation handle to link the initial receive to the subsequent send
Variable<CorrelationHandle> enrollHandle = new Variable<CorrelationHandle>();

// Arguments received from the initial call to EnrollStudent
Variable<EnrollStudentArgs> enrollArgs = new Variable<EnrollStudentArgs>();

// The generated student Id
Variable<Guid> studentId = new Variable<Guid>();

// The namespace I'm using for all of the contracts etc
string ns = "http://www.morganskinner.com";

// This Receive activity kicks off the whole workflow
Receive receive = new Receive
{
    CanCreateInstance = true,
    CorrelatesWith = enrollHandle,
    OperationName = "EnrollStudent",
    ServiceContractName = XName.Get("IStudent", ns),
    Value = new OutArgument<EnrollStudentArgs>(enrollArgs)
};

The CorrelationHandle is used to link the initial Receive and the subsequent SendReply together. Here I’ve also defined that the output value from the Receive should be stored away in a variable called enrollArgs. This variable will be globally scoped in the workflow (not that it needs to be – I could have defined it locally). I’ve also defined the studentId variable which will be used to store the unique Id created by the code. In a real solution you might just pass this Guid up from the client in the initial call – however that would have been less for me to demonstrate so I chose this method instead.

What we have defined is now nearly enough to so the first stage of the workflow – the initial Receive, the computation of the Guid and the SendReply to pass this Guid back to the client. I say nearly enough – there is one more thing we need to define up front but I’m going to gloss over that just for a moment and show the workflow up to this point…

return new Sequence
{
    Variables = { enrollHandle, enrollArgs, studentId },
    Activities =
    {
        receive,
        new WriteLine { Text = "Assigning a unique GUID" },
        new Assign<Guid>
        {
            To = new OutArgument<Guid>(studentId),
            Value=new InArgument<Guid> (Guid.NewGuid())
        },
        new WriteLine { Text = new InArgument<string>(env=>
string.Format("GUID assigned was '{0}'", studentId.Get(env)))} , new SendReply { CorrelatesWith = enrollHandle, Value = new InArgument<Guid>(studentId), Request=receive, AdditionalCorrelations = {{ "StudentIdQuery", operationHandle}}, CorrelationQuery = new CorrelationQuery { SelectAdditional = { studentIdOutputQuery } } }
}
}

So here I’ve defined a Sequence, and inside it are the receive activity we’ve defined earlier (this cannot be defined inline as the SendReply activity needs a reference to it). I’ve added some WriteLine activities so that we can see what the workflow is up to, and then defined the SendReply which is where it all gets a bit hectic.

The first three properties are fairly simple – CorrelatesWith defines a correlation handle link between the SendReply and the earlier Receive. The Value is simply what we want to return from the SendReply, and Request=receive links this SendReply with the earlier Request activity.

Now, if you remember the API I wanted to expose, I want to return a Guid from the initial call and use this same Guid on subsequent calls to identify the workflow instance that should process that call. That’s what the AdditionalCorrelations and CorrelationQuery properties are defined for. What I’m defining here is a query that will strip out the return value (i.e. the Guid) and associate this with another correlation handle – in this case the operationHandle.

So, in order to get this to compile I need to define the following before defining the Sequence shown above…

// The correlation handle defined after the initial receive/send pair
Variable<CorrelationHandle> operationHandle = 
new Variable<CorrelationHandle>(); // Define a message context XPathMessageContext messageContext = new XPathMessageContext(); messageContext.AddNamespace("local", ns); // This query retrieves the output argument sent from the SendReply MessageQuerySet studentIdOutputQuery = new MessageQuerySet { { "StudentId", new XPathMessageQuery("sm:body()/ser:guid", messageContext) } }; studentIdOutputQuery.Name = "StudentIdQuery";

Here I’ve defined the second correlation handle which will be used by the subsequent calls from the client (AddExamResults and Graduate). Then I’ve defined the message context which is used when defining the XPathMessageQuery objects that are used within the MessageQuerySet. I’ve defined a namespace prefix of “local” to refer to the namespace of my classes – we’ll need this again later in the example when retrieving values passed up from the client.

The MessageQuerySet object is used to define which bits are stripped from the message. In this case I have just one named value - “StudentId”, and this comes from the rather cryptic “sm:body()/ser:guid” XPath statement.

Now I’m not here to give you a lesson in XPath (and indeed you wouldn’t want one from me either!). If like me you think that XPath is akin to Voodoo Magic then I wouldn’t blame you.

The sm:body()/ser:guid implies that I’m looking throughout the ‘body’ of the message message for the ‘guid’ element from the ‘ser’ namespace and returning that. If you look at the actual data returned from the SendReply activity on the wire you would see something like the following (I captured this using the most excellent Fiddler2 tool)…

HTTP/1.1 200 OK
Content-Length: 203
Content-Type: text/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Fri, 04 Sep 2009 09:15:52 GMT

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <guid xmlns="
http://schemas.microsoft.com/2003/10/Serialization/">
      ddafc236-bec4-4745-bc6d-882f5ab3b051
    </guid>
  </s:Body>
</s:Envelope>

Here you can probably infer that we’re using ‘ser’ as a prefix for the Serialization namespace. It makes sense now, but I don’t much like magic prefixes – there are a bunch that are currently defined and used within the framework but are not currently documented. The documentaton should be sorted before WF 4 is released. If you’re interested these constants currently live on the XPathMessageContext class.

With all that defined we’re at the point where we should have a working Receive and SendReply (and of course a few other activities). Now it gets even more interesting.

Now I want to be able to call either the AddExamResults or the Graduate methods. These both pass the Guid along on the wire, but the former also passes other information too. In order to get to the right workflow instance I therefore need to extract the student Id from these calls and match it up to my earlier extract.

For this to work I’ll need another couple of message query sets…

// Retrieve the AddExamResultsArgs.StudentId passed to AddExamResults
MessageQuerySet studentIdQuery = new MessageQuerySet
{
    { "StudentId", 
new XPathMessageQuery
("sm:body()/local:AddExamResultsArgs/local:StudentId",
messageContext) } }; // And this one retrieves the value passed in to the Graduate method MessageQuerySet graduateId = new MessageQuerySet { { "StudentId", new XPathMessageQuery("sm:body()/ser:guid",
messageContext) } };

The first message query set defines the XPath that will strip out the value of the StudentId property from the AddExamResultsArgs class. You’ll see that I’ve included the ‘local’ namespace prefix for both of these elements – it’s necessary, you won’t get anywhere without it.

The second message query set is used for the call to the Graduate method – here I’m just passing up a simple Guid and so need to strip just that Guid out of the message.

With a couple more variables defined (a boolean flag ‘finished’ and a variable to record the exam results) and two more receive activities we’re pretty much sorted…

// A variable used to hold the exam results just added
Variable<AddExamResultsArgs> examResultArgs = 
new Variable<AddExamResultsArgs> ( ) ; // Flag indicating that we're done Variable<bool> finished = new Variable<bool> { Default = false };
// This recieve activity waits for the AddExamResults operation to be called
Receive receiveAdd = new Receive
{
    OperationName = "AddExamResults",
    ServiceContractName = XName.Get("IStudent", ns),
    Value = new OutArgument<AddExamResultsArgs>(examResultArgs),
    CorrelatesWith = operationHandle,
    CorrelationQuery = new CorrelationQuery
    {
        Select = studentIdQuery
    }
};

// And this activity waits for the Graduate operation to be called
Receive receiveStop = new Receive
{
    OperationName = "Graduate",
    ServiceContractName = XName.Get("IStudent", ns),
    CorrelatesWith = operationHandle,
    CorrelationQuery = new CorrelationQuery
    {
        Select = graduateId
    },
    Value = new OutArgument<Guid>(studentId)
};

In the above I’ve defined the other receive operations that my workflow will process. These again need to define their operation names and the name of the service contract to which they belong, and the only other tricky part is to define the correlation information. The AddExamResults operation is passed an argument of the AddExamResultsArgs type, so I need to strip out the AddExamResultsArgs.StudentId in order to correlate this message with the workflow. That’s just what the CorrelatesWith and CorrelationQuery are doing.

The second operation is just passed a Guid, so again I’m using a query that will retrieve the Guid passed to the Graduate operation.

Finally we get to the rest of the workflow…

return new Sequence
{
    Variables = { enrollHandle, enrollArgs, studentId, 
finished, operationHandle },

        …

    new While
    { 
        Condition = ValueExpression.Create<bool>
( env => !finished.Get(env)), Body = new Pick { Branches = { new PickBranch { Variables = {examResultArgs}, Trigger = receiveAdd, Action = new WriteLine
{
Text = new InArgument<string>
(env=>string.Format
("Student '{0}' received '{2}' in '{1}'",
examResultArgs.Get(env).StudentId,
examResultArgs.Get(env).ExamName,
examResultArgs.Get(env).Mark)
)
} }, new PickBranch { Trigger = receiveStop, Action = new Sequence { Activities = { new Assign<bool> { To = new OutArgument<bool>(finished), Value = new InArgument<bool>(true) }, new WriteLine { Text = new InArgument<string>
(env=>string.Format("Student {0} Graduated",
studentId.Get(env)))} } } } } } }
}
 
Here I’ve defined the While loop, and within it a Pick activity which is similar to the Listen from WF 3.x. It has any number of child branches and will wait until one of the branches completes. In this case my branches are awaiting calls to WCF methods that I’ve defined by using Receive activities.

The first branch schedules the Receive activity that awaits a call on the AddExamResults method (and writes this data out to the console when it arrives), and the second branch waits for the Graduate call to be made and then sets the boolean ‘finished’ flag so that the workflow completes.

Example – Hosting the Workflow

Now we have a workflow we need to host it. Again I’m just using code rather than XAML to show you all the stuff you need.

// Define the base address for the service
// Note - use the full name of the PC rather than localhost here
string serviceBaseAddress = "http://dev10-vpc:8080/StudentService";

using (WorkflowServiceHost host = 
new WorkflowServiceHost(GetStudentWorkflow(),
new Uri(serviceBaseAddress))) { // Define the service namespace string ns = "http://www.morganskinner.com"; // And add the endpoint host.AddServiceEndpoint(XName.Get("IStudent", ns),
new BasicHttpBinding(),
serviceBaseAddress); ServiceMetadataBehavior smb =
host.Description.Behaviors.Find<ServiceMetadataBehavior>(); // If none, add one if (smb == null) smb = new ServiceMetadataBehavior(); // Setup properties of the service metadata behavior smb.HttpGetEnabled = true; smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15; // And set it host.Description.Behaviors.Add(smb); // Now open for business... host.Open(); Console.WriteLine("StudentService is ready."); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Press <enter> to exit."); Console.ResetColor(); Console.ReadLine(); host.Close(); }
 
Here I’ve defined the base address for the service to include the physical name of the machine I’m running on (dev10-vpc). If you use ‘localhost’ it will still work fine, however Fiddler2 won’t be able to pickup the traffic and display it for you.

I then construct a WorkflowServiceHost and add an appropriate endpoint to it. After that I’ve defined a metadata behaviour to permit clients to get the service metadata over Http (I could have added a metadata endpoint/binding instead). After that it’s just a case of opening the service and waiting for calls. You don’t need a .config file as all of the configuration has been done in code.

The last (and you’ll be glad to know, easiest!) part is creating the client.

Example – Creating a client

Construction of the client is simple. Add a service reference to the service (in my instance http://dev10-vpc/StudentService) and then call it. I’ve deliberately used different client instances here to prove that

static void Main(string[] args)
{
    Console.WriteLine("Press a key to connect to the student service");
    Console.ReadLine();

    Guid studentId;

    using (StudentService.StudentClient client = new StudentService.StudentClient())
    {
        studentId = client.EnrollStudent(
new EnrollStudentArgs { Name = "Fred Bloggs",
DateOfBirth = new DateTime(1980, 09, 04) }).Value; Console.WriteLine("Student Id returned was '{0}'", g); } Console.WriteLine("Now press a key to call the service again"); Console.ReadLine(); ReportExamResults(studentId, "Maths", 90); ReportExamResults(studentId, "English", 80); ReportExamResults(studentId, "Woodwork", 100); Console.WriteLine("Now press a key to call the Graduate operation"); Console.ReadLine(); // And finally graduate... using (StudentService.StudentClient client = new StudentService.StudentClient()) { client.Graduate(studentId); } } private static void ReportExamResults(Guid studentId, string examName, int mark) { using (StudentService.StudentClient client = new StudentService.StudentClient()) { client.AddExamResults(new AddExamResultsArgs
{
StudentId = studentId,
ExamName = examName,
Mark = mark
}); } }
 
So there you have it. A worked example of correlation in Workflow 4 Beta 1. If you want to download the code for this sample please click here.
Originally posted by Morgan Skinner on 09 September 2009 here.