If you are looking for the demo files from my CodeCamp presentation in the Netherlands you can download the notes here and the complete sample code here. No PowerPoint, after all this was a CodeCamp 
And when my ISP is fully operational again
the demo can be viewed online here.
Enjoy, I enjoyed organizing the CodeCamp and doing this presentation.
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
The whole persistence model has changed quite a but for WF4.
The persistence class out of the box is called SqlWorkflowInstanceStore and as the name suggests it saves workflow data in either SQL Server 2005 or 2008. It is based on the InstanceStore class to if you prefer some other store all you need to do is subclass the InstanceStore and create your own.
So what can we do with the SqlWorkflowInstanceStore?
We can attach it to either a WorkflowApplication or a WorkflowServiceHost and persist workflows when we want. Notice I left out the WorkflowInvoker. This can be used to run only short lived workflows and doesn’t support persistence.
The case of the WorkflowApplication is quite simple.
var instanceStore = new SqlWorkflowInstanceStore(connStr);
WorkflowApplication app = new WorkflowApplication(workflow);
app.InstanceStore = instanceStore;
app.Run();
Okay its a little more involved then that.
First of all you need to create a database to store the workflow state in. There are a couple of SQL files in the “C:\Windows\Microsoft.NET\Framework\v4.0.21006\SQL\en” folder. The two you need are SqlWorkflowInstanceStoreSchema.sql and SqlWorkflowInstanceStoreLogic.sql. I created a batch file to quickly recreate my persistence database like this:
osql -E -S .\sqlexpress -Q "Drop Database WorkflowInstanceStore"
osql -E -S .\sqlexpress -Q "Create Database WorkflowInstanceStore"
osql -E -S .\sqlexpress -d WorkflowInstanceStore -i SqlWorkflowInstanceStoreSchema.sql
osql -E -S .\sqlexpress -d WorkflowInstanceStore -i SqlWorkflowInstanceStoreLogic.sql
So with all this in place we still need to tell the WorkflowApplication when to persist the workflow. There are several ways to to this but one is to use the PersistableIdle callback. This will only fire when an InstanceStore has been provided. Options are to persist and unload the workflow, just unload the workflow or do nothing at all. The callback is just another function so you can make whatever decisions you like.
app.PersistableIdle = e => PersistableIdleAction.Persist;
Another option is to use the Persist activity. This will allow the workflow to decide on extra persistence points regardless of the workflow being idle.
How about the WorkflowServiceHost?
With the WorkflowServiceHost we get a couple of different choices. First of all we can just create a SqlWorkflowInstanceStore and set it like this:
var workflow = new Workflow1();
var baseAddress = new Uri("http://localhost:8080/MyWorkflow");
var host = new WorkflowServiceHost(workflow, baseAddress);
var connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowInstanceStore;Integrated Security=True;Pooling=False";
var instanceStore = new SqlWorkflowInstanceStore(connStr);
host.DurableInstancingOptions.InstanceStore = instanceStore;
host.Open();
Console.WriteLine("Listening...");
Console.ReadLine();
host.Close();
Simple but it doesn’t give us any control over when workflows are persisted. We don’t get quite the same control as with a WorkflowApplication in this case, all we can do is set a few timeout values using the WorkflowIdleBehavior like this:
var workflowIdleBehavior = new WorkflowIdleBehavior();
workflowIdleBehavior.TimeToPersist = TimeSpan.FromSeconds(10);
workflowIdleBehavior.TimeToUnload = TimeSpan.FromMinutes(1);
host.Description.Behaviors.Add(workflowIdleBehavior);
We can’t make any decisions based, just set time the workflow is idle before it is persisted and the same before it is unloaded.
Suppose we want some more control over the way the SqlWorkflowInstanceStore behaves. We can using the SqlWorkflowInstanceStoreBehavior. This is actually the same class as is used through the config file.
var connStr = @"Data Source=.\sqlexpress;Initial Catalog=WorkflowInstanceStore;Integrated Security=True;Pooling=False";
var behavior = new SqlWorkflowInstanceStoreBehavior(connStr);
behavior.InstanceCompletionAction = InstanceCompletionAction.DeleteNothing;
behavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;
behavior.InstanceEncodingOption = InstanceEncodingOption.None;
host.Description.Behaviors.Add(behavior);
So that is all there is to it?
Not quite there is a little complexity when it comes to using the same SqlWorkflowInstanceStore with multiple WorkflowApplication instances. Note that the WorkflowServiceHost automatically takes care of this so no need to worry about that.
By default a SqlWorkflowInstanceStore will only work with a single WorkflowApplication. If you try to use it with mutiple workflow you can get a InstancePersistenceCommandException with the following message:
SqlWorkflowInstanceStore does not support creating more than one lock owner concurrently. Consider setting InstanceStore.DefaultInstanceOwner to share the store among many applications.
The trick is to set the DefaultInstanceOwner of the SqlWorkflowInstanceStore. The code isn’t hard but not exactly obvious either.
var instanceStore = new SqlWorkflowInstanceStore(connStr);
var instanceHandle = instanceStore.CreateInstanceHandle();
var createOwnerCmd = new CreateWorkflowOwnerCommand();
var view = instanceStore.Execute(instanceHandle, createOwnerCmd, TimeSpan.FromSeconds(30));
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
// Do whatever needs to be dome with multiple WorkflowApplications
var deleteOwnerCmd = new DeleteWorkflowOwnerCommand();
instanceStore.Execute(instanceHandle, deleteOwnerCmd, TimeSpan.FromSeconds(30));
The key is the CreateWorkflowOwnerCommand that needs to be executed at the start. And when you use the CreateWorkflowOwnerCommand just make sure not to forget the DeleteWorkflowOwnerCommand otherwise all workflow will remain locked by the owner and can’t be reloaded by another SqlWorkflowInstanceStore
Quite a but more flexible than we had before but, specially with the WorkflowApplication, also quite a bit more work and not always as obvious as I would like it.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
This one bit me today a bit unexpectedly.
I was comparing two Uri’s and was getting unexpected matches between two Uri’s that where quite clearly not the same. Turns out the when comparing Uri’s the Fragment, or anchor or part after the #, is not part of the comparison.
So the following unit test passes even though I would have expected it to fail!
[TestMethod]
public void TestTwoEqualUrisWithDifferentAnchorShouldNotBeEqual()
{
var baseUri = new Uri("http://www.test.com");
var uri1 = new Uri(baseUri, "#anchor1");
var uri2 = new Uri(baseUri, "#anchor2");
Assert.AreEqual(uri1, uri2);
Assert.AreNotEqual(uri1.ToString(), uri2.ToString());
}
Note that when comparing uri1 and uri2 they are equal even though they use two different anchor tags. Comparing the ToString() results does show the difference though. Wasted another bit of time on this piece of trivia 
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
recently I had to send a bunch of identical email messages for the CodeCamp we are organizing. Now there are plenty of ways to do this but I decided to write a little program using C# to send them. And just to make sure I can find the code next year 
var from = new MailAddress("<<your email>>", "<<your name??");
var to = new MailAddress("<<destination address>>", "<<their name>>");
var message = new MailMessage(from, to);
message.Subject = "The subject";
message.Body = "The message body";
message.IsBodyHtml = true;
var host = "smtp.gmail.com";
var client = new SmtpClient(host, 587);
client.EnableSsl = true;
client.Credentials = new NetworkCredential("<<your username>>", "<<your password>>");
client.Send(message);
Easy as that.
Few things to note. I used a different sender address as we wanted all replies to end up in a shared mailbox instead of mine. Worked just fine even though I used my own account details to send.
There is a check on the number of messages send in a short time span. Not sure what the limit is, it seemed something like 100 per minute, but I had to add some sleep time between messages in order to send them all. No big deal but something to keep in mind.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Als eerste: de sessies voor de CodeCamp 2009 zijn bekend en staan online op http://www.codecamp.nl We denken dat het een erg interessante mix van sessies is, met genoeg sessies voor iedereen om een aantal interessante onderwerpen voor iedereen. De agenda ziet er nu als volgt uit:
09:30 - 10:45
Around .net framework 4.0 in an hour (Ronald Guijt)
ASP.Net - MVC 2.0 (Sander Gerz)
Windows Mobile en het werken met data (Arjan van Huizen)
11:00 - 12:15
ADO.NET EF 4.0 (Kurt Claeys)
SharePoint Nightmares (Marianne van Wanrooij)
iPhone development met jQTouch (Maurice de Beijer)
13:15 - 14.30
VSTO 2010 met Office 2010 (Hassan Fadili)
Modulaire Silverlight apps met Prism (Timmy Kokke)
Microsoft Surface Development (Freena Eijffinger & Dennis Vroegop)
14:45 - 16:00
VSTS 2010 (Pieter de Bruijn)
Windows Identity Foundation (Michiel van Otegem)
SQL Azure (Marcel Meijer)
Naast deze sessies hebben we ook nog de OpenSpace sessies. Daar hebben we geen agenda voor maar dat ligt nu eenmaal in de aard van een OpenSpace gebeuren. In het kort komt het neer op het volgende: als je iets hebt waar je graag met een aantal mensen over wilt discussieren, schrijf je dat 's ochtends op een flip-over. Mochten mensen dat interessant vinden, dan kunnen ze een stem uitbrengen op dat onderwerp. In de lunchtijd (van 12:15 - 13:15) is dan de keuze aan de mensen waar ze heen gaan en aan welke discussie ze mee willen gaan doen. Heeft jouw sessie genoeg stemmen dan komen de mensen vanzelf wel naar je toe, zo niet: dan is je sessie blijkbaar niet interessant voor een grote groep. Het idee is dat we een aantal van deze sessies tegelijkertijd hebben zodat mensen kunnen kiezen wat ze doen. De inhoud van de lunch sessies laten we dus volledig aan de bezoekers over!
Denk eens na over sessies of onderwerpen en discussieer mee met je mede-ontwikkelaars over jouw favoriete onderwerp!
Ik kan haast niet wachten tot het 21 november is..