Workflow
The question of what Windows Workflow Foundation version 4 means for developers currently developing using WF recently came up. As I mentioned before WF 4 is a complete rewrite and doesn’t use any of the existing WF 3 classes. The design of WF 4 is even quite different from the design of WF 3.
So are WF 3 developers completely left in the dark and have to start from scratch? And what about their existing applications, will they still run on .NET 4 or are they stuck in .NET 3.5 until the end of times?
Fist of all let me address why I believe Microsoft could make such a radical change. Keep i mind this is just my personal opinion and I might be completely wrong
.
WF 3 was never widely adopted so I believe the team felt they could do this and not create too much of a problem. Personally I don't think the low adoption of WF 3 had anything to do with the technology. Sure it has its problems but most people never got to the point that they ran into real technical issues. Instead most people looked, didn't understand and quit. So IMHO the main problem was far more a question if evangelism that technology.
So what is the big issue with a migration from WF 3 to WF 4?
One big issue with WF 4 is that Microsoft decided not to release a state machine implementation. In WF 3 there was a choice between state machine and sequential workflows. In WF 4 there is a choice between sequential and flow chart. And in WF 3 a state machine turned out to be far more flexible and useful and quite a few cases are not really covered by the WF 4 model. There are a few workarounds to do something like a state machine in WF 4 but they are not pretty. That said, a number of state machine workflows could very well be done using a flow chart.
As far as backward compatibility the story is a bit more complicated. WF 4 ships with an interop activity you can use to host custom WF 3 activities in a WF 4 workflow. How good or bad this is? I really don't know yet but given how different the two designs are I fear it is very limited.
But WF 3 developers need not worry about their existing projects!
There is however a second side to the compatibility story. .NET 4 will ship both with the WF 3 and the WF 4 runtimes. The WF 3 runtime will not be prominent but it will still be there so all existing WF 3 applications will continue to run as is. And because it is part of the current .NET framework I suspect this is a legal requirement (but then I am no lawyer so could be wrong here). So there is no reason for existing workflow developers to panic just yet
.
So existing workflow developers should start learning WF 4 for future projects but can continue to keep using their existing WF 3 solutions for the foreseeable future.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Passing parameters into a workflow is similar in WF4 as it was in WF3.
In both case a Dictionary<string, object> is passed in when creating the workflow instance object. Alternatively when using the WF4 WorkflowInvoker you can pass it into the Invoke() method.
This is quite simple but I have never been a fan of the very loose coupling here. basically on the outside you have to create a dictionary with as key something inside of the workflow. No compile time checking. No improvement where, unless you consider the somewhat easier unit testing with the WorkflowInvoker.
The actual behavior between WF3 and WF4 is quite different however.
In Wf3 if you pass in a key that doesn’t match up you get a System.Reflection.TargetInvocationException with a System.InvalidOperationException inner exception with the text: “This operation can not be performed at runtime.”. Great help in debugging 
In WF4 on the other hand you get no error at all. The value is just ignored so it is even harder to detect typos. Well I guess not every change can be an improvement. And just in case you where thinking “But a unit test will take care of that right?” keep in mind that workflows tend to be complex and long running things not very well suited to unit testing.
Starting a workflow with a parameter:
Shared Sub Main()
Dim inArgs As New Dictionary(Of String, Object)
inArgs("Name") = "Maurice"
Dim myInstance As WorkflowInstance = New WorkflowInstance(New Sequence1(), inArgs)
myInstance.OnCompleted = AddressOf OnWorkflowCompleted
myInstance.OnAborted = AddressOf OnWorkflowAborted
myInstance.OnUnhandledException = AddressOf OnWorkflowUnhandledException
myInstance.Run()
syncEvent.WaitOne()
End Sub
So where is this Name value actually stored?
Well in WF3 that would have been stored in a property, possibly a dependency property, or a field. The types must match, exactly.
In WF4 the notion of properties and fields is gone. No more dependency properties to tie everything together. So instead we have Variables and Arguments. Basically a variable is just like a variable in a function, only here it holds some value for an activity. Big difference is that a Variable is in scope, i.e. visible, to all nested activities as well. So basically it is a way of sharing state between different activities.
To allows us to pass data into a workflow, or get something out, we need to use an Argument.
In this case I have defined two arguments, the Name to pass into the workflow and the Greeting that will be the result of the workflow.
Getting the result out is done in the OnCompleted delegate. This delegate is passed a WorkflowCompletedEventArgs which in turn contains another dictionary with all out argument.
Shared Sub OnWorkflowCompleted(ByVal e As WorkflowCompletedEventArgs)
Dim greeting As String = e.Outputs("Greeting")
Console.WriteLine(greeting)
syncEvent.Set()
End Sub
Again not very different from WF3 where we used the WorkflowCompleted event and the OutputParameters dictionary from the WorkflowRuntime.
Conclusion
While there are quite a few differences in the details the basic approach remains the same. And as the old approach with dictionaries was rather error prone I consider this a missed chance 
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
With WF3 there was a central workflow runtime environment called the WorkflowRuntime and used to manage the lifetime of workflow instances. In WF4 this central class no longer exists and we manage individual workflows.
To compare the two, this is what a minimal console application looks like in WF3. It basically runs a workflow and prints a message when done:
static void Main(string[] args)
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{
Console.WriteLine("The workflow has completed.");
};
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
instance.Start();
Console.WriteLine("Waiting for the workflow to complete.");
Console.ReadLine();
}
}
The same functionality in WF4 looks very different:
static void Main(string[] args)
{
WorkflowInstance myInstance = new WorkflowInstance(new Sequence1());
myInstance.OnCompleted += delegate(WorkflowCompletedEventArgs e)
{
Console.WriteLine("The workflow has completed.");
};
myInstance.Run();
Console.WriteLine("Waiting for the workflow to complete.");
Console.ReadLine();
}
The most important thing to note is that there is no longer a WorkflowRuntime object, instead we create a new WorkflowInstance object passing in a workflow instance (compared to a workflow type before).
There are some benefits as well as drawbacks to not having a WorkflowRuntime.
The main benefit is that it makes creating and running workflows much easier. No longer do we need to keep track of a singleton and use that to do everything. Another benefit is we can register event handler per workflow instance, makes it easier to do different things for different types of workflows. In fact the OnCompleted isn’t an event but a delegate so you can add only a single handler.
The drawback is that general configuration. Like recording workflow errors, tracking execution or workflow persistence must be done for each instance while before we could just do it on the workflow runtime.
Some things haven’t changed. In both WF3 and WF4 running the workflow is done asynchronously so we need to wait, in both cases done using a Console.ReadLine().
Synchronous execution
With WF3 it was hard to have a workflow execute synchronously. Basically we had to add a different scheduler to the workflow runtime and explicitly tell the scheduler to execute the workflow. Of course this would affect every workflow created through the same WorkflowRuntime.
With WF4 there is a much easier model using the WorkflowInvoker class. All we need to do is call WorkflowInvoker.Invoke() passing in the workflow object and the workflow will execute synchronously on the same thread. Not only very useful for running the odd workflow but also for unit testing as any WorkflowElement, the base class for all activities, can be executed this way.
static void Main(string[] args)
{
WorkflowInvoker.Invoke(new Sequence1());
Console.WriteLine("The workflow has completed.");
Console.ReadLine();
}
Nice right!
Enjoy.
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
As you may have heard Windows Workflow Foundation 4 is not an upgrade from Windows Workflow Foundation 3 (or 3.5). The version numbers might suggest that the previous version was quite mature but in fact it refers to the version of the .NET framework. In fact Windows Workflow Foundation 3 was the first version and 3.5 added only very few features and some bug fixes. And as WF 4 is a complete rewrite we should approach it as a new product and forget just about everything we already know about WF 3.
That might be a bit surprising, after all we still have activities and a workflow runtime right?
Well wrong actually!
There no longer is a class named WorkflowRuntime. Creating workflows is done by creating a WorkflowInstance object. This object is raises events for that workflow and only for that specific workflow.
But surely there is still an Activity base class?
Yes there is. But this is a completely new class that has nothing to do with the class Activity that was used with WF 3. In fact the new Activity class is in a new assembly and namespace, the full name is System.Activities.Activity in the assembly System.Activities.dll. The “old” activity class was System.Workflow.ComponentModel.Activity in the System.Workflow.ComponentModel.dll assembly. The old class still exists in .NET 4 so old workflows will still run but the new classes have no relation with the old classes.
So basically forget all you knew about the behavior of WF 3 
So lets take a brief look at creating a new workflow 4 style workflow.
When we start Visual Studio 2010 and select File/New/Project we get the new project tab. Select the Workflow node and you will see the four new project types
More about the different types in another blog post. For now just select the “Sequential Workflow Console Application”.
What we end up with is a simple project with just 2 files, the program.cs and the Sequence1.xaml. The latter contains the workflow. Note there is no choice between code or xaml workflows, the designer only works with xaml based workflows. Also note that the extension is no longer XOML but has changed to XAML.
The Sequence1.xaml workflow is still empty and looks like this:
When we open the Toolbox we see the new collection of basic activities. These are also completely new. For now just drag a WriteLine activity onto the Sequence in the designer. The goal of the WriteLine activity is just what the name suggest: write a line of text to some form of output stream. When we open the property sheet with the WriteLine activity selected we see there are just three properties: the DisplayName, the Text and the TextWriter.
The DisplayName is actually the name used on the design surface and used during the design of the workflow.
The Text property contains the text we want to print. In the property sheet we see a textbox with the text “<Enter an expression>”. This really is an expression editor where you can type in code. So if you want to enter a simple fixed text you must use quotation marks around it. This must always be a valid Visual Basic statement. yes that isn’t a typo Visual Basic, not C#, even if you are developing a C# project.
So in this case just add “Hello Workflow 4” including the quotation marks.
The TextWriter property is the text stream to write the text to. This is also an expression, just like the Text, and is optional. When not specified the text will be written to Console.Out which will do fine for now.
These expression windows in VB are completely new and a big change from WF3. Where we previously had to bind to property binding or something similar to add custom code we can now type it directly into the designer.
With this in place we are ready to run the workflow. Press Ctrl+F5 and with a bit of luck you should see the output appear in the console window. You might need a bit of luck because it appears the current bits are still a bit unstable and on my laptop running Windows 7 the designer tends to lock up quite a bit 
In the next post more about how the workflow is created and started at runtime.
Attached are the PowerPoint presentation and samples from my WF with WCF presentation in Timisoara, Romania. Thanks to Ineta for providing the funding for this.
Attached are the PowerPoint presentation and samples from my state machine workflow presentation in Timisoara, Romania. Thanks to Ineta for providing the funding for this.
Last year Marcel and I worked on a problem using a TransactionScopeActivity inside a WCF ReceiveActivity. I blogged a number of time about our finding, for example here.
Last week I heard from Marcel that a hotfix was finally available that solves this problem. He blogged about it here. Great work to see there is a fix for this serious problem.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
And now on Twitter at http://twitter.com/mauricedb.
Last week lots of MVP, myself included, where in Redmond visiting Microsoft on our yearly MVP pilgrimage. The MVP Summit is always a great event. Not only do we get to talk to the MS team members who actually design and build all the software we love and use but we also get to meet each other. And with MVP’s being located around the world that is kind of rare as well.
Despite this being a great event I still have very mixed feelings about this last MVP summit. The problem isn’t the events itself or the people I met, that was al great
. No the problem was in some of the technological decisions made in product groups. Unfortunately I can’t divulge details yet because I have signed an NDA but as soon .NET 4.0 as beta 1 is made public I will be able to blog about some of these decisions. And rest assured I will!
The reason we really need to is the way these important changes are communicated. Or should I say not communicated. Some very important changes where not communicated at all and could only be derived by bullet points missing from PowerPoint slides. Only when directly asked about the missing bullet point would someone say “Yes that is right, we decided to …. NDA … because … NDA …”. Sorry about the NDA part
.
I can only say that these decisions are a big problem and I know several MVP’s who decided to skip other sessions on the same product as a result. I didn’t, as far as I am concerned ignoring a problem is not going to make it go away, but I really think this product team needs to reevaluate this decision and the general future trend.
Despite this hiccup I am very exited about lots of the things I got to see and am really looking forward to .NET 4 and Visual Studio 2010. Now if we only could have a beta today
. When we will have a first beta is still a big question though. If a date was announced I missed it, the best I heard was a quarter and is quite a time span.
Enjoy.
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
A while a go I wrote some posts about unit testing Windows Workflow foundation Activities, you can find the posts here and here. Fortunately I am not the only one who thinks unit testing is important as you can find various other blog posts about it like this one from Ron Jacobs.
But Cliff Simpkins has taken this one step further and just blogged about a new MSDN virtual lab "Using Test-First Development with WF"! Excellent work and I recommend doing the lab, I know I did! And while you are at it, there are a number of other interesting labs you can do.
Read more about Cliffs announcement here or go straight to the lab here.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Yesterday I uploaded a new version of the SQL Server Compact Workflow Persistence Service to code.msdn.com. No major changes this time just two new features to get it more in line with the standard SqlWorkflowPersistenceService.
- I added a GetAllWorkflows() function that returns all persisted workflow's in the database.
- I added a LoadExpiredTimerWorkflowIds() function that will return a list of workflow instanceId's of all workflow's with an expired timer.
Both new functions should help make the SqlCeWorkflowPersistenceService even more like the out of the box SqlWorkflowPersistenceService. And as before it is completely free
.
You can download the source code for the SqlCeWorkflowPersistenceService here: http://code.msdn.microsoft.com/SqlCeWFPersistence.
PS should you need to develop a custom WorkflowPersistenceService for some other database this would be a good starting point!
Enjoy.
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
In the next version of workflow we are going to leave the XOML extension behind us. Creating workflow's or custom activities using markup will create a XAML file instead of the now familiar XOML file. That doesn't mean we are moving to exactly the same same format as WPF as WF is quite different in its nature.
But at the same time we seem to getting a new file extension named XAMLX. So what is this XAMLX all about? Well it turns out this is a markup file containing both the WCF service declaration as well as the WF service implementation. Is this a good thing? Well I am really not sure yet. XAML markup works great for hierarchical items like WPF forms or nested WF activities but a service interface is a different thing.
Guess I will have to think about this one some more.
Enjoy the CTP!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
The PDC wasn't all hard work, we sure had some fun too
.
As always there where a number of product group dinners. This year the C#, VB and F# product groups joined up and had a dinner together. I am at the right bottom, check Anders sitting at the left back end of the table. Lots of other team members and MVP's where there and we had a great time!
The party this year was at Universal Studio which is always a nice place to go to. Before we went there was another product group dinner to attend and afterward I drove with Beth and Alan to Universal Studio.
Last time Beth was in the Netherlands for the SDC I drove her around and this time she got to drive me around. The drive over was fun and fast, Beth drives like the Italian in her
and this was with her "slow" car as she also has a tuned Subaru. This is Beth getting chased by a zombie.

Some of the creatures in Universal Studio where really creepy and well done.
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Want to download and watch all PDC content? Then there are a couple of ways to get at them. The official way if to go through the session agenda at the conference site. See https://sessions.microsoftpdc.com/public/timeline.aspx. You see all sessions but it takes a bit of work. Another nice way is through Channel 9 by using the following feed: http://channel9.msdn.com/posts/pdc2008/RSS/?tag=videos.
But the best as far as I am concerned is using the list compiled by Greg Duncan. Check this list http://coolthingoftheday.blogspot.com/2008/10/pdc2008-quick-video-link-list.html. And it even has the Visual Basic source code used to generate the list 
Enjoy the videos!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Windows Azure is the Microsoft awnser to the Google App Engine and Amazon EC2. Basically it is a platform in the cloud for developing web applications. It isn't just about hosting ASP.NET applications but also about services like SQL Server and SharePoint Server. You basically use Visual Studio and the languages like C# or VB ou already know. Development is local using a development server that mimincs the Azure network, just like Cassini mimicts IIS during development. And ofcouse Azul also contains workflow services 
Windows Azure is the development, hosting, and management environment of the Azure Services Platform, which enables you to run applications at Internet scale while leveraging the skills and tools you use today.
Windows Azure Tools for Microsoft Visual Studio extend Visual Studio to enable the creation, building, debugging, running, and packaging of scalable services on Windows Azure.
see http://blogs.msdn.com/cloud/ or http://www.azure.com for more details.
interseting stuff 
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Last week during my Chalk & Talk session at the SDC one of the attendees was having problems with workflow persistence and collections of custom objects. Now I never got to see his code so I am not sure what the problem was but maybe this simple sample will convince him that this should work.
So we need a simple type, I decided to keep thing really simple and use the following:
using System;
namespace WorkflowConsoleApplication1
{
[Serializable]
public class Person
{
public string Name { get; set; }
}
}
And we need a simple workflow like this. The two code activities print the contents of the collection and the delay activity is there to make sure everything is persisted to the database so I can stop and restart the application.
The code in the workflow is pretty simple as well 
using System;
using System.Collections.Generic;
using System.Workflow.Activities;
namespace WorkflowConsoleApplication1
{
public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
}
public List<Person> People { get; set; }
private void printPeople_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("The people in workflow {0} are:", WorkflowInstanceId);
foreach (var person in People)
{
Console.WriteLine(person.Name);
}
Console.WriteLine();
}
}
}
And the code in the main program is simple to
. First we need to add the SqlWorkflowPersistenceService the runtime.
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
string connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowPersistence;Integrated Security=True";
SqlWorkflowPersistenceService persistence = new SqlWorkflowPersistenceService(
connStr, true, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(15));
workflowRuntime.AddService(persistence);
and when we start the workflow we need to add the collection of people like this:
List<Person> people = new List<Person>();
people.Add(new Person() { Name = "Maurice de Beijer" });
people.Add(new Person() { Name = "John Doe" });
people.Add(new Person() { Name = "Arthur Dent" });
Dictionary<string, object> parms = new Dictionary<string, object>();
parms["People"] = people;
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
typeof(WorkflowConsoleApplication1.Workflow1), parms);
instance.Start();
Just to make sure we could see when a workflow was loaded from disk I added the following:
workflowRuntime.WorkflowLoaded += (s, e) =>
{
Console.WriteLine("Workflow {0} was just reloaded", e.WorkflowInstance.InstanceId);
};
And the result is:
Just before this run I started the application and stopped it while the delayActivity was waiting so there was an extra record in the persistence database. During this run we can see the workflow being rehydrated and the collection of people being present. So everything works just as expected.
So pretty much the only thing that cab go wrong here is with the serialization. The workflow runtime uses the binary serializer sp all types need to be Serializable. And this includes all other types that are referenced somehow. And the somehow includes event handlers, something people tend to forget as this is not very visible as an object reference.
Hope this helps.
www.TheProblemSolver.nl
[f2}
Part 1
Part 2
Part 3
Part 4
In the previous blog posts we made sure we could have multiple versions of the same workflow running side by side. This ability is one of the more powerful concepts of WF and a real must have for long running business applications.
A quick recap.
Always version your assemblies by giving them a strong name. Make sure the runtime can find each version of the assembly by pointing the CLR to the right version using the configuration\runtimeassemblyBinding\dependentAssembly\assemblyIdentity\codeBase in your App.Config (or Web.Config in the case of a web application). And make sure you use all types and interfaces from the same version as the workflow or, somewhat easier, stick to using basic CLR types when sending messages.
Great, but what about fixing bugs?
All the versioning is very nice but the simple fact is that sooner or later you are going to find a bug in your code and need to fix a specific assembly. In that case it would not be very nice if the workflow would keep on running with the buggy code. No in that case you would very much like to be able to dehydrate the worfklows and have them use the patched version of the assembly instead of the original one.
Fortunately this is easy to do, and again due to the standard binary serialization format Windows Workflow Foundation uses, completely standard .NET.
Again the trick is versioning the assembly and using the App.Config to redirect the runtime to the correct version. So just as I demonstrated in the previous posts I need to strongly sign the assembly. Next when we want to fix a bug in the assembly we need to update the version number and redirect the CLR to the new version. The last part is done using the following config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="WorkflowLibrary1" publicKeyToken="8afb6d596a769080" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="1.0.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
To demonstrate the effect the workflow below is started with a parameter that indicates the assembly version it was when started and prints this, long with the current assembly version, on the screen. as you can see the first workflow was started with version 1.0.0.0 but the assembly actually executing is updated to 1.0.1.0. In contrast the second workflow was created using the assembly 1.0.1.0.
So why bother to update the version number in the first place?
After all you could just leave the version number as is and replace the assembly. The main disadvantage is that it makes it harder to see which version was executing when a new bug report comes in. Was this the patched assembly and is the bug still there under specific circumstances? On other words: was the bug fix buggy
. Or is the system still using the original assembly, and was the update not done correctly? Best just to avoid these kind of problems and make sure you can see which version either by looking at the assembly file properties or by having an error handler print all loaded assemblies, including version number.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Part 1part 2Part 3Part 4 You may recall my previous posts about using the TransactionScopeActivity within a ReceiveActivity and all the nasty issues we ran into. Just in case you want to read them again: one, two and three.
As it turn out this is an intentionally not supported scenario at the moment. If we read the docs for the ReceiveActivity we can find the following note:
To ensure that persistence performs properly and does not persist transient messages, make sure that child activities of the ReceiveActivity do not persist by themselves. This can occur if the child activities go idle when a persistence provider was created with UnloadOnIdle set to true, for instance.
The note uses the UnloadOnIdle as an example but the persistence part is just as much true for the TransactionScopeActivity as this persists the state. Of course there is no similar warning in the TransactionScopeActivity docs
and the activity validation fails to warn us either so there is definitely some room for improvement here.
But rumor has it that this improvement is coming
The rumor says that not only will we be able use a TransactionScopeActivity inside of a ReceiveActivity but we can even go a step further in being able to flow a transaction from the client through the WCF request into the workflow and have the TransactionScopeActivity participate in the same transaction. Nice but I guess we will have to wait for the PDC before we get all the details.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Part 1
Part 2
Part 3
Part 4
In the first article of this series I demonstrated how to get multiple versions of a workflow running side by side in the same workflow runtime. The most important thing was that you need to keep every version of the assembly around and use the assemblyBinding element in the app.config to let the runtime know where each version was on disk. Once done life was good 
In the second part I demonstrated how a HandleExternalEventActivity was version dependent and you needed to use the version specific service to send a message to the workflow. It worked but the as the code was not exactly pretty life was just ok
.
What is wrong with the HandleExternalEventActivity?
Well there is nothing really wrong with the HandleExternalEventActivity but it is a very thin layer over the actual workflow structures it tries to hide. And these structures are the workflow queuing mechanism! Internally everything is turned into a message and send through a queue. So if this is only a thin abstraction layer why not use the original API in the first place.
That is exactly what I advise, leave the external data exchange mechanism for what it is and just create a custom workflow activity.
What does it take to implement the same behavior using a custom activity? Not a whole lot actually so lets take a look.
Above is the new workflow with the custom activity.
Because the custom activity is used as the first child in one of the branches of a ListenActivity it must implement the IEventActivity interface and we must override the Execute method to do something once a message is found in the queue.
using System;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
namespace WorkflowLibrary1
{
public partial class MyActivity : Activity, IEventActivity
{
public static string TheQueueName = "MyActivityQueueName";
protected override ActivityExecutionStatus Execute(
ActivityExecutionContext executionContext)
{
WorkflowQueuingService wqs = executionContext.GetService<WorkflowQueuingService>();
WorkflowQueue queue = wqs.GetWorkflowQueue(QueueName);
object data = queue.Dequeue();
Console.WriteLine("Received {0} in {1}", data, GetType().Assembly.FullName);
return base.Execute(executionContext);
}
public IComparable QueueName
{
get { return TheQueueName; }
}
public void Subscribe(
ActivityExecutionContext parentContext,
IActivityEventListener<QueueEventArgs> parentEventHandler)
{
WorkflowQueuingService wqs = parentContext.GetService<WorkflowQueuingService>();
WorkflowQueue queue = null;
if (wqs.Exists(TheQueueName))
queue = wqs.GetWorkflowQueue(TheQueueName);
else
queue = wqs.CreateWorkflowQueue(QueueName, true);
queue.RegisterForQueueItemAvailable(parentEventHandler);
}
public void Unsubscribe(
ActivityExecutionContext parentContext,
IActivityEventListener<QueueEventArgs> parentEventHandler)
{
WorkflowQueuingService wqs = parentContext.GetService<WorkflowQueuingService>();
WorkflowQueue queue = wqs.GetWorkflowQueue(TheQueueName);
queue.UnregisterForQueueItemAvailable(parentEventHandler);
}
}
}
I am not going to explain the details except that the message is read in the Execute and is printed as is along with the assembly version. And guess what, If I create multiple versions of the workflow and run them side by side life is good
.
Sending the data was easy too with only the following code:
static void SendEvent1(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
instance.EnqueueItem(MyActivity.TheQueueName, 1, null, null);
}
However the data send in this simple example is only an integer. Lets see what happens when we use a custom object instead of the single integer.
The case of the custom message type
In the previous example everything worked just fine because we only send in a real simple data type, an integer. However when we switch to a custom type things are less perfect
.
For this example I am using the following data type:
namespace WorkflowLibrary1
{
public class MyData
{
public MyData(int data)
{
TheData = data;
}
public int TheData { get; set; }
public override string ToString()
{
return string.Format("Data = {0}", TheData);
}
}
}
Still real simple but non the less a custom type we can version along with the workflow and its activities. The code to send the message becomes as follows:
static void SendEvent1(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
MyData data = new MyData(1);
instance.EnqueueItem(MyActivity.TheQueueName, data, null, null);
}
Again not a spectacular change as we only substitute the integer for an object of type MyData. The activity execute changes to the following:
protected override ActivityExecutionStatus Execute(
ActivityExecutionContext executionContext)
{
WorkflowQueuingService wqs = executionContext.GetService<WorkflowQueuingService>();
WorkflowQueue queue = wqs.GetWorkflowQueue(QueueName);
MyData data = (MyData)queue.Dequeue();
Console.WriteLine("Received {0} in {1}", data, GetType().Assembly.FullName);
return base.Execute(executionContext);
}
Again no big change, all we are doing is casting the data from the queue to be of type MyData. When we run this with a workflow started using the latest version everything is just fine but when I send a message to a workflow version 1.0.0.0 we receive the following InvalidCastException message:
Unable to cast object of type 'WorkflowLibrary1.MyData' to type 'WorkflowLibrary1.MyData'
That message seems kind of weird as it is claiming that we cannot cast MyData to MyData!. Weird as this may seem it is completely true!
The problem, and things would have been clearer of the message include this information is that we cannot cast between two different versions of the same type as they are really different types.
The solution
Just like the previous time the solution is to create an object of the same type as was used in the custom workflow activity. The concept is pretty much the same as last time with the ExternalDataExchangeService and requires a bit of reflection.
static void SendEvent2(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
Assembly assembly = instance.GetWorkflowDefinition().GetType().Assembly;
Type type = assembly.GetType(typeof(MyData).FullName);
object data =Activator.CreateInstance(type, new object[] {1});
instance.EnqueueItem(MyActivity.TheQueueName, data, null, null);
}
using this code both the workflow and the runtime are perfectly happy. That said, personally I don't really like having to resort to reflection every time
So instead of using typed objects you might just want to resort to using basic framework objects which will remain the same version until a major .NET framework upgrade. One easy way to send data is just embed it in an XML document or, just as the workflow parameters, a Dictionary<string, object> and pass that along.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Part 1
Part 2
Part 3
Part 4
In my previous post I demonstrated how to keep multiple versions of an assembly around and how to use the assemblyBinding element in the app.config to let the runtime load multiple versions of a worklfow. In the end we had both workflows, the first in assembly 1.0.0.0 and the second in assembly 2.0.0.0, running and life seemed to be good 
So is there more to write on the subject? Yes unfortunately there are still some potential problems that need to be addressed
.
The pitfalls of External Data Exchange
Lets take a look at what happens if we add a HandleExternalEventActivity to the mix. This HandleExternalEventActivity can be used to have a workflow react to input from an external data exchange service, sometimes called local communication.
Lets change the workflow to reflect the following:
In this workflow I am waiting for either a DelayActivity to fire or an event to be raised from an external service. Like before the activities are emended in a permanent loop so the workflow is never finished.
I have kept the external data exchange service real simple. The interface looks like this:
using System;
using System.Workflow.Activities;
namespace WorkflowLibrary1
{
[ExternalDataExchange]
public interface IMyService
{
event EventHandler<MyEventArgs> TheEvent;
}
}
The implementation like this:
using System;
namespace WorkflowLibrary1
{
public class MyService : IMyService
{
public event EventHandler<MyEventArgs> TheEvent;
public void OnTheEvent(Guid instanceId)
{
if (TheEvent != null && instanceId != Guid.Empty)
{
MyEventArgs args = new MyEventArgs(instanceId, DateTime.Now);
TheEvent(null, args);
}
}
}
}
And the event parameter looks like this:
using System;
using System.Workflow.Activities;
namespace WorkflowLibrary1
{
[Serializable]
public class MyEventArgs : ExternalDataEventArgs
{
public DateTime FiredAt { get; set; }
public MyEventArgs(Guid instanceId, DateTime firedAt)
: base(instanceId)
{
FiredAt = firedAt;
}
}
}
Not much complexity there
.
In the main function the workflow runtime is created and configured again. This time we also need to add the MyService as follows:
ExternalDataExchangeService edes = new ExternalDataExchangeService();
workflowRuntime.AddService(edes);
MyService myService = new MyService();
edes.AddService(myService);
The complete main function is at the bottom of the post but the most important things are the two Guids. The variable instanceId1 holds the WorkflowInstanceId from a workflow version 1.0.0.0 while the variable instanceId2 holds a WorkflowInstanceId from a workflow version 2.0.0.0. Both of these have been created during two previous runs and are saved by the SqlWorkflowPersistenceService added to the runtime. So lets see what happens when I run the application.

As we can see from the screenshot both workflows, the first in assembly 1.0.0.0 and the second in assembly 2.0.0.0, are running together just fine. So lets see what happens when we raise the event TheEvent as declared in the external data exchange interface.
As we can see from the screenshot above the second version of the workflow receives the event just fine but version 1.0.0.0 doesn't and instead we receive the following exception:
Event "TheEvent" on interface type "WorkflowLibrary1.IMyService" for instance id "c9592a1b-e703-4726-b9bb-16410a7aaaad" cannot be delivered.
Now the workflow did manage to receive the event when it was just created, in fact it could up until the moment we recompiled the application and deployed version 2.0.0.0. Neither the workflow nor the service has changed so what gives?
Underneath the covers of the HandleExternalEventActivity
To understand the problem we must first understand a bit more about the internals of the HandleExternalEventActivity. When we created the ExternalDataExchange interface we declared an event taking a parameter derived from ExternalDataEventArgs. And when configuring the HandleExternalEventActivity we specified an event name so everything works using .NET events right? Well no, wrong!
In fact pretty much everything in Windows Workflow Foundation works based on queues. In fact due to the long running nature and the fact you don't really know when thing will execute it must do so. In fact that is the reason why the event parameter must be marked with the Serializable attribute. In fact the ExternalDataExchangeService watches every for every possible event and converts every event into a queued message.
Okay nice to know but how does that help with this problem?
Well the thing is it needs to be able to find the correct queue to send the message and that is where things get interesting. When we look at WF queues we see that queue names are of type IComparable. Now most of the time when creating a queue the easiest thing to do is use a string or a guid as the queue name. And as both implement IComparable this is perfectly legal. But in the case of the ExternalDataExchangeService and the HandleExternalEventActivity both need to be able to construct the same queue name based upon the interface and event name. And this is where the EventQueueName enters.
The EventQueueName also implements IComparable and is used internally to uniquely identify a queue name. And when the EventQueueName checks if two queues are the same it doesn't just use the interface name and the event name but it also compares the assemblies both are defined in. So in this case the workflow version 1.0.0.0 is creating a queue that contains the fact that it is from version 1.0.0.0 as part of the contract while the runtime only uses the last version this creating a queue that contains version 2.0.0.0 as part of the queue name.
We can use the following code to pint the queue information from each workflow:
static void PrintQueues(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
ReadOnlyCollection<WorkflowQueueInfo> queues = instance.GetWorkflowQueueData();
foreach (var queue in queues)
{
EventQueueName queueName = queue.QueueName as EventQueueName;
if (queueName != null)
{
Assembly assembly = queueName.InterfaceType.Assembly;
Console.WriteLine(queueName);
Console.WriteLine(queueName.InterfaceType.Assembly.FullName);
Console.WriteLine();
}
}
}
With this as the result:

The solution
The original function used raise the event looked like this:
static void SendEvent1(WorkflowRuntime workflowRuntime, Guid instanceId)
{
MyService myService = workflowRuntime.GetService<MyService>();
myService.OnTheEvent(instanceId);
}
The reason this is failing should now be apparent. After all the main program binds to the version of WorkflowLibrary1 it was build against or version 2.0.0.0. So the GetService() call returns a service object that created a queue name containing version 2.0.0.0 as part of its name and cannot call a HandleExternalEventActivity that creates a queue version 1.0.0.0.
So the solution is to create the correct service object. Doing so isn't complicated but does unfortunately requires some reflection because we need to work with the same type from an older assembly.
static void SendEvent2(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
Assembly assembly = instance.GetWorkflowDefinition().GetType().Assembly;
ExternalDataExchangeService edes = workflowRuntime.GetService<ExternalDataExchangeService>();
Type type = assembly.GetType(typeof(MyService).FullName);
object myService = edes.GetService(type);
if (myService == null)
{
myService = Activator.CreateInstance(type);
edes.AddService(myService);
}
MethodInfo mi = type.GetMethod("OnTheEvent");
mi.Invoke(myService, new object[] { instanceId });
}
This code first gets an assembly reference to the assembly the actual workflow was defines in. Depending on the workflow this is either going to return assembly version 1.0.0.0 or version 2.0.0.0. Once we have this reference we retrieve the correct service type from the assembly. This type is different for each assembly so when we ask the ExternalDataExchangeService for the service it will try to return one with the correct type information. If this isn't found yet it will return null and we can use the Activator.CreateInstance() to create the correct type adding it to the ExternalDataExchangeService for next time.
Next we use reflection to execute the OnTheEvent we defined to fire the event for the workflow which now uses the correct version information when creating the queue name and everything works just fine.
The screenshot above shows that both workflow versions are able to receive events again.
Conclusion
The code above works and solves the problem but is not very nice. It assumes that the ExternalDataExchange is defined in the same assembly as the workflow, something that doesn't need to be the case. So is there a better solution? Yes but that is the subject of another blog post.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
The complete main program:
using System;
using System.Workflow.Activities;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using WorkflowLibrary1;
using System.Reflection;
using System.Collections.ObjectModel;
namespace WorkflowConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
ExternalDataExchangeService edes = new ExternalDataExchangeService();
workflowRuntime.AddService(edes);
MyService myService = new MyService();
edes.AddService(myService);
string connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowPersistence;Integrated Security=True";
SqlWorkflowPersistenceService persistence = new SqlWorkflowPersistenceService(connStr,
true, TimeSpan.FromSeconds(15), TimeSpan.FromMinutes(1));
workflowRuntime.AddService(persistence);
workflowRuntime.ServicesExceptionNotHandled += (sender, e) =>
{
Console.WriteLine(e.Exception.Message);
};
workflowRuntime.StartRuntime();
Guid instanceId1 = new Guid("c9592a1b-e703-4726-b9bb-16410a7aaaad");
Guid instanceId2 = new Guid("482e2742-e7c7-45e2-bcd3-8f894d200733");
//WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
//instanceId2 = instance.InstanceId;
//instance.Start();
bool done = false;
while (!done)
{
try
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.KeyChar)
{
case '1':
SendEvent2(workflowRuntime, instanceId1);
break;
case '2':
SendEvent2(workflowRuntime, instanceId2);
break;
case '5':
PrintQueues(workflowRuntime, instanceId1);
break;
case '6':
PrintQueues(workflowRuntime, instanceId2);
break;
case '0':
done = true;
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
workflowRuntime.StopRuntime();
}
}
static void SendEvent1(WorkflowRuntime workflowRuntime, Guid instanceId)
{
MyService myService = workflowRuntime.GetService<MyService>();
myService.OnTheEvent(instanceId);
}
static void SendEvent2(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
Assembly assembly = instance.GetWorkflowDefinition().GetType().Assembly;
ExternalDataExchangeService edes = workflowRuntime.GetService<ExternalDataExchangeService>();
Type type = assembly.GetType(typeof(MyService).FullName);
object myService = edes.GetService(type);
if (myService == null)
{
myService = Activator.CreateInstance(type);
edes.AddService(myService);
}
MethodInfo mi = type.GetMethod("OnTheEvent");
mi.Invoke(myService, new object[] { instanceId });
}
static void PrintQueues(WorkflowRuntime workflowRuntime, Guid instanceId)
{
WorkflowInstance instance = workflowRuntime.GetWorkflow(instanceId);
ReadOnlyCollection<WorkflowQueueInfo> queues = instance.GetWorkflowQueueData();
foreach (var queue in queues)
{
EventQueueName queueName = queue.QueueName as EventQueueName;
if (queueName != null)
{
Assembly assembly = queueName.InterfaceType.Assembly;
Console.WriteLine(queueName);
Console.WriteLine(queueName.InterfaceType.Assembly.FullName);
Console.WriteLine();
}
}
}
}
}
Part 1
Part 2
Part 3
Part 4
One of the cool features of Windows Workflow Foundation is that it allows long running processes. And not just long running as in a few minutes but really long running as in a few years
. This is possible true the use of the SqlWorkflowPersistenceService, or in fact any derived class from WorkflowPersistenceService, which is going to save the state of a workflow to disk when it is not actually busy.
So that is pretty cool but it is kind of unlikely that your programs are not going to change over a year so in all likelihood you are going to be deploying newer versions of your assemblies while there are multiple workflow's active. In order to allow multiple versions of a workflow to run we need to understand what is going on under the covers.
To demonstrate the behavior I am going to use a small project with the following layout. The WorkflowConsoleApplication1 is the host and is configured to use SqlWorkflowPersistenceService. The WorkflowLibrary1 is the project containing the actual workflow and this is the project we are going to version.
The main program is pretty simple and looks like this:
static void Main(string[] args)
{
using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
string connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowPersistence;Integrated Security=True";
SqlWorkflowPersistenceService persistence = new SqlWorkflowPersistenceService(connStr,
true, TimeSpan.FromSeconds(15), TimeSpan.FromMinutes(1));
workflowRuntime.AddService(persistence);
workflowRuntime.ServicesExceptionNotHandled += (sender, e) =>
{
Console.WriteLine(e.Exception.Message);
};
workflowRuntime.StartRuntime();
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
instance.Start();
Console.WriteLine("Press ennter to stop");
Console.ReadLine();
workflowRuntime.StopRuntime();
}
}
Basically it sets up the SqlWorkflowPersistenceService, starts the runtime, starts a new workflow and waits for the user to terminate the program. It also prints any exceptions that might occur in a workflow runtime service like the SqlWorkflowPersistenceService.
The workflow is real simple. It just loops forever and prints a message that includes the assembly version number every 10 seconds.
What we are going to do is start this program so we have a first instance in the persistence store, update the workflow assembly to version two an have version 1 and 2 run side by side.
The WorkflowPersistenceService part.
The WorkflowPersistenceService actually persists, or dehydrates as it is called, a workflow using the binary serializer. This means that when it recreates, or dehydrates as this is called, the workflow instance it is done in a assembly version depended manner. So the first thing to be aware of is the standard .NET versioning behavior.
For a .NET assembly to be versionable the first prerequisite is that it has a string name. The rules are simple, no strong name = no versioning and the first assembly found will be the one that is used. Now there is a potential problem here because the binary serializer uses the most compact form possible and doesn't store field names, only their value. The object is responsible for reading the data from a stream in the correct order and size. No problem as long as the same type that was serialized is actually used to deserialize an object. But suppose a newer type is used that actually expects extra fields? Well it is going to try to read some data that is not actually there and the process is going to fail
.
Versioning assemblies is important!
So in order to be able to version the WorkflowLibrary1 assembly we need to add a strong name. This is done in the project settings on the Signing tab as follows:
When we run the application we can see from the following output that the workflow version 1 is running just fine
.
Once we have stopped the application its time to upgrade to version 2.0.0.0. Now I am not even going to make any changes to the workflow, all I am going to do is change the assembly major version number of the WorkflowLibrary1 project to 2. This is actually stored as part of the assembly and thus causes the project to recompile.
If we run the application again we are going to see the following output:
While version 2.0.0.0 of the workflow will run just fine version 1.0.0.0 cannot be loaded and the SqlWorkflowPersistenceService reports the following error via the WorkflowRuntime ServicesExceptionNotHandled event.
Could not load file or assembly 'WorkflowLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8afb6d596a769080' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
This error is the result of the binary serializer seeing that the workflow was dehydrated with version 1.0.0.0 of the WorkflowLibrary1 so it wants to load that version. And that is nowhere to be found as we only have version 2.0.0.0 which is not good enough.
Enabling side by side execution
The first ting we need to do is make sure we have both versions of the assembly WorkflowLibrary1 available. In order to do so we need to create a folder in the bin\Debug folder with the name Version_1_0_0_0. This is the folder where I am going to keep a copy of the WorkflowLibrary1.dll version 1.0.0.0.
Next we need to tell the .NET runtime where to look for version 1.0.0.0 of the assembly by adding this information to the app.config of our application. This is standard .NET material and in no way specific to Windows Workflow Foundation. The app.config looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="WorkflowLibrary1" publicKeyToken="8afb6d596a769080" />
<codeBase version="1.0.0.0" href="Version_1_0_0_0/WorkflowLibrary1.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
With both versions of the assembly and configuring the runtime so it knows where to find the first version of the assembly both versions of the workflow will run happily together
.
So how about the GAC?
In this example I use a private location to store multiple versions of the assembly but I could also have used the Global Assembly Cache, of GAC for short, to do this. Both mechanisms allow an assembly to be versioned so which is better? Normally the GAC should be used by assemblies used with multiple applications. In this case, and this is probably the case with most workflow assemblies, the WorkflowLibrary1 assembly only used by our host and not by other applications making a private location the proper place. That said there are certainly scenarios, like shared custom activity assemblies, where the GAC would be the proper place.
So are we all set?
No unfortunately not quite because there are some more issues that might crop up with versioning. So stay tuned for more 
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
More Posts
Next page »