The Problem Solver

Tell me and I will forget
Show me and I will remember
Involve me and I will understand
- Confucius -

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 Smile

 

And when my ISP is fully operational again Sad the demo can be viewed online here.

 

Enjoy, I enjoyed organizing the CodeCamp and doing this presentation.

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | with no comments
Filed under: , , , ,

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

Posted by Maurice | 1 comment(s)
Filed under: , , ,

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 Sad

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | with no comments
Filed under:

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 Smile

 

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

Posted by Maurice | with no comments
Filed under: ,

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..

Posted by Maurice | with no comments
Filed under: , ,

Flowcharts are a nice addition to Windows Workflow Foundation 4. They allow for a lot of pretty complex behavior that is hard to do in a sequential workflow. In WF 3 we used to model these complex behaviors as state machine workflows. That worked but they weren't really state machines or event driven and things could get a bit tricky.

 

Enter the flowchart in Windows Workflow Foundation 4

One of the good things is that a flowchart is not another workflow type. No it is just another activity to drop in a workflow. So you are free to combine sequential work with a flowchart in one workflow. For example you can have a Sequence activity containing a Flowchart activity and that in turn can contain another Sequence activity which can contain another …. I guess you get the picture.

Below a Flowchart containing a mix of a FlowSwitch and a FlowDecision activity, which are only usable in a Flowchart, and some other general activities like a WriteLine and an Assign activity.

image

 

So far so good.

The one thing I don’t like is the FlowSwitch activity

The problem with the FlowSwitch is that the activity takes an expression determining the branch to execute and each branch has a case property to determine which branch is executed. Sounds like a good plan except when you start trying things. What I did was add a Random variable named rnd to the workflow so different branches would execute. To do this I added the following expression to the FlowSwitch:

rnd.Next(5)

Next in each of the first 3 cases I entered 1, 2 and 3 leaving the 4th as default. Sounds good right?

image

Well not quite!

When I ran the sample I would only get Default printed to the console. Well I guess with Random that is possible to try again. And again.

 

Hmm something must be wrong here.

It turns out all the cases are of type string and the expression is of type Int32. Those are never going to match! But there are no compile or runtime errors either. I am sure this is going to bite me some time in the future Sad

Of course the FlowSwitch should just to a ToString() on the expression result, anything else would be pointless as any comparison fails. But as it doesn’t we have to do so ourselves. So I changed the Expression to:

image

and everything works as expected.

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , , ,

When using Windows Workflow Foundation 4 you often need to enter expression in some activity property. The new thing is these expressions are all in the Visual Basic dialect regardless of what language you project is in. So when developing a project in C# the expressions are sill in Visual Basic.

Weird right?

Well not really when you think about it!

After all power business users are expected to be able to modify workflows. And guess what their favorite tools are. That would be Microsoft Excel and Word. And what do they use to create macros? Right Visual Basic for Applications is their language of choice.

 

So if they use VBA letting them enter VB into expression dialogs is a far more natural fit than having them use C#.

 

A case for the Delay activity

So lets take a look at the delay activity. This activity takes 1 input property Duration of type Time Span. So I guess that means our VBA power users are going to have to enter something like if they want to wait 5 seconds:

image

Now for some reason I suspect that is not the first thing they are going to type. In fact they are more likely to time something like “0:0:05” instead. Not very VB like right?

Well it turns out workflow is smart enough to realize it can convert the string literal to a Time Span and use this. So when our power users does the following it just works Smile

image

Nice, I like this!

 

Enjoy WF4!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 9 comment(s)
Filed under: , , , , ,

Op 21 november 2009 organiseren de SDN, de dotNED User Group en VBcentral.nl samen de derde Nederlandse Code Camp. Een unieke dag, voortkomend uit een unieke samenwerking. Kenmerkend aan deze dag is, dat het een evenement is dóór ontwikkelaars en vóór ontwikkelaars!

We zijn op zoek naar collega ontwikkelaars die het leuk vinden om ook een sessie voor hun rekening te nemen. Er is ruimte voor de meer traditionele sessies, maar bijvoorbeeld ook voor Chalk & Talk sessies. In principe is zijn de mogelijke onderwerpen zeer divers en worden door ons op voorhand niet beperkt. Ben jij geïnteresseerd om ook een sessie te doen? Laat het ons weten! Ook als je geen of nog niet zoveel sprekerservaring hebt ben je meer dan welkom. Indien je dat wilt willen we je hier best mee helpen.

Het evenement vindt plaats in Rotterdam en meer informatie kun je vinden op de Code Camp website:www.codecamp.nl

We zien graag jouw sessievoorstel tegemoet! Stuur deze zo snel mogelijk naar: andre@obelink.com.

Een lijstje van onderwerpen waar deelnemers ondermeer om vragen:

  • Architectuur/Design patterns
  • Frameworks
  • Surface
  • .NET 4/3.5
  • Visual Studio 2010
  • VSTS
  • Open Source
  • SharePoint
  • ASP.NET/MVC/Web development/Silverlight
  • Windows 7
  • iPhone
  • DNN
  • VSTO
  • LINQ
  • SQL Server 2008
  • Geneva
  • BizTalk
  • Windows Presentation Foundation
  • Windows Communication Foundation
  • Windows Workflow Foundation

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | with no comments
Filed under: , ,

Creating parameters for a workflow is quite easy to do in WF 4. Just open the arguments tab in the workflow and add each input or output parameter you need and the required type.

image

 

Now in WF 3 passing data to a workflow was done using a Dictionary<string, object> to specify the input. This is very flexible but also very brittle as it is all magic of key names matching property names. This approach still works as the following code demonstrates.

var input = new Dictionary<string, object>();
input["FirstName"] = "Maurice";
input["LastName"] = "de Beijer";
var result = WorkflowInvoker.Invoke(new Workflow1(), input);
Console.WriteLine(result["Greeting"]);

 

However WF4 has a new approach that includes full type safety. Every time you create an in or out argument the workflow will also get a corresponding public property. These properties are of type InArgument<T> but they contain an implicit cast from T so the code is quite easy as shown below.

var workflow = new Workflow1();
workflow.FirstName = "Maurice";
workflow.LastName = "de Beijer";
 
var result = WorkflowInvoker.Invoke(workflow);
Console.WriteLine(result["Greeting"]);

 

Much cleaner!

Unfortunately the OutArgument Greeting is there but printing it’s contents to the console only results in the type being printed. There is also a Get() method but this requires an ActivityContext to be passed. So in the case of output we are still restricted to using the Dictionary<string, object>.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | with no comments
Filed under: , ,

One real nice feature in Windows 7 is the boot from VHD capability. Real simple to do once you know the basics. And these basics are all covered in 2 blog posts by Scott Hanselman. So rather that repeat the information I am just going to point to his blog posts.

  1. Step-By-Step: Turning a Windows 7 DVD or ISO into a Bootable VHD Virtual Machine
  2. Less Virtual, More Machine - Windows 7 and the magic of Boot to VHD

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

www.twitter.com/mauricedb

Posted by Maurice | with no comments

Op 21 november 2009 organiseren de SDN, Stichting dotNed en VBcentral samen het derde Nederlandse Code Camp. Dit is een dag lang met code, code sharing, freaking en gezellig samenzijn. Een evenement door ontwikkelaars, voor ontwikkelaars. De regie ligt voor een belangrijk deel bij de deelnemers! Het aantal plaatsen voor deelnemers is wel beperkt tot maximaal 150. Wacht dus niet te lang met beslissen want voor je het weet is er geen plaats meer. Vergeet bij je aanmelding niet op te geven welke onderwerpen je interesse hebben. Bovendien nodigen we iedereen graag uit om zelf een sessie in te vullen.


Op de website, www.codecamp.nl, vind je meer informatie en kan je je aanmelden.

Looking for the slides and samples from my two presentations at the Nationale Office Dag in Ede? You can download them from here.

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | with no comments
Filed under: , ,

In the previous blog posts, here, here and here, I demonstrated how to  use WCF from WF4. This same some more about sending multiple messages to the same workflow, AKA Workflow Correlation.

 

One of the ugly parts of Windows Workflow Foundation 3 was the message correlation part when you used WCF to send multiple messages to the same workflow.

When using WF3 you where forced to use one of the context bindings like BasicHttpContextBinding or WSHttpContextBinding. Not only that but you also had to retrieve and set the correlation context using the IContextManager and set it to some arcane guid. This guid was the workflow instance ID but having to use that on the client means the client had to be very aware of the server technology used, something that goes against the principals of WCF. Using something in the message itself as to route the message to the correct workflow was just not possible with the build-in activities.

 

Fortunately the WF4 story is much better. No longer do we need to use the “context” bindings but the standard WCF bindings will do just fine. And the client doesn’t have to work with the workflow instance id but can pass some more logical data, like an invoice number, that will be mapped to the correct workflow. Much better!

 

To demonstrate this we first need to expand the workflow so it can receive multiple messages for the same workflow. The input for both Receive activities will still be the same person class from the previous example and we will use the persons ID to route the messages.

 

The new workflow definition looks like this:

private static WorkflowElement CreateWorkflow()
{
    var result = new Sequence();
    var input = new Variable<Person>();
    result.Variables.Add(input);
    XNamespace ns = "http://tempuri.org";
 
    var handle1 = new Variable<CorrelationHandle>();
    result.Variables.Add(handle1);
 
    var handle2 = new Variable<CorrelationHandle>();
    result.Variables.Add(handle2);
 
    var receive1 = new Receive()
    {
        OperationName = "Operation1",
        ServiceContractName = ns + "MyService",
        Value = new OutArgument<Person>(input),
        AdditionalCorrelations = {                   
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle1)}
        },
        CorrelationQuery = CreateCorrelationQuery(),
        CanCreateInstance = true
    };
    result.Activities.Add(receive1);
 
    var write1 = new WriteLine()
    {
        Text = new InArgument<string>(env => string.Format("\tThe workflow was called with '{0}'.", input.Get(env)))
    };
    result.Activities.Add(write1);
 
    var reply1 = new SendReply()
    {
        Request = receive1,
        Value = new InArgument<string>("The result")
    };
    result.Activities.Add(reply1);
 
 
    var receive2 = new Receive()
    {
        OperationName = "Operation2",
        ServiceContractName = ns + "MyService",
        Value = new OutArgument<Person>(input),
        AdditionalCorrelations = {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle2)}
        },
        CorrelationQuery = CreateCorrelationQuery()
    };
    result.Activities.Add(receive2);
 
    var write2 = new WriteLine()
    {
        Text = new InArgument<string>(env => string.Format("\tThe workflow was called again with '{0}'.", input.Get(env)))
    };
    result.Activities.Add(write2);
 
    var reply2 = new SendReply()
    {
        Request = receive2,
        Value = new InArgument<string>("The second result")
    };
    result.Activities.Add(reply2);
 
    return result;
}

 

This is very similar to the previous workflow except we have two Receive activities and only the first is configured to allow the message to create a new instance.

 

The trick in getting the second message routed to the same workflow is by setting the CorrelationQuery on the two Receive activities. This is done in the CreateCorrelationQuery() function as follows:

private static CorrelationQuery CreateCorrelationQuery()
{
    var result = new CorrelationQuery();
    var xpath = new XPathMessageQuery()
    {
        Namespaces = new XmlNamespaceManager(new NameTable()),
        Expression = "//sample:Person/sample:Id"
    };
 
    xpath.Namespaces.AddNamespace("sample", "urn:WF4Sample:person");
 
    var messageQuerySet = new MessageQuerySet()
    {
        Name = "RequestCorrelation"
    };
 
    messageQuerySet.Add("PersonId", xpath);
    result.Select = messageQuerySet;
 
    return result;
}

The important part here is that the MessageQuerySet key value and the related Expression XPath must produce the same result for both request. Something that is easy here as I am sending in the same person object twice.

 

So the regular client has hardly any changes. All that is needed is the addition of the second operation to the ServiceContract like this:

[ServiceContract]
interface MyService
{
    [OperationContract]
    Operation1Response Operation1(Person request);
    [OperationContract]
    Operation1Response Operation2(Person request);
}

And calling the second operation like this.

static void Main(string[] args)
{
    var binding = new BasicHttpBinding();
    var endpoint = new EndpointAddress("http://localhost:8090/Sequence1/Operation1");
    var factory = new ChannelFactory<MyService>(binding, endpoint);
    var proxy = factory.CreateChannel();
 
    var request = new Person()
    {
        Id = 23,
        FirstName = "Joe",
        LastName = "Regular",
        BirthDate = new DateTime(1975, 7, 1)
    };
    var result = proxy.Operation1(request);
    Console.WriteLine(result.Value);
 
    //request.Id = 99;
    request.FirstName = "Maurice";
    request.LastName = "Chevalier";
    request.BirthDate = new DateTime(1888, 9, 12);
 
    result = proxy.Operation2(request);
    Console.WriteLine(result.Value);
 
    Console.WriteLine("Regular client is done.");
    Console.ReadLine();
}

Note that I cam completely free to change the person object as long as I use the same Id. Should I change the Id the WorkflowServiceHost would try and find an existing workflow with the changed person Id, fail and throw a FaultException with message “The requested operation could not complete because instance key '4d7e998a-4cff-4743-3dd4-2afd5578383c' could not be found.”. Not quite sure where the Guid comes from but there is no workflow with that instanceId.

The good thing here is that the client doesn’t need to be aware of the workflow implementation on the server and the server can use “regular” message data to determine the correct workflow to send the message to.

 

The Workflow client has about the same change. In this case the Send activity is duplicated but other that that there is very little change and certainly no code that makes the client workflow aware of the service workflow.

private static WorkflowElement CreateWorkflow()
{
    var result = new Sequence();
 
    XNamespace ns = "http://tempuri.org";
 
    var endpoint = new Endpoint()
    {
        Uri = new Uri("http://localhost:8090/Sequence1/Operation1"),
        Binding = new BasicHttpBinding()
    };
 
    var response = new Variable<string>();
    result.Variables.Add(response);
 
    var handle = new Variable<CorrelationHandle>();
    result.Variables.Add(handle);
 
    var person = new Person()
    {
        Id = 7,
        FirstName = "Maurice",
        LastName = "de Beijer",
        BirthDate = new DateTime(1962, 8, 24)
    };
 
    var request = new Send()
    {
        OperationName = "Operation1",
        Endpoint = endpoint,
        AdditionalCorrelations =
        {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        ServiceContractName = ns + "MyService",
        Value = new InArgument<Person>(person)
    };
    result.Activities.Add(request);
 
 
    var reply = new ReceiveReply()
    {
        Request = request,
        Value = new OutArgument<string>(response)
    };
    result.Activities.Add(reply);
 
    var write = new WriteLine()
    {
        Text = new InArgument<string>(response)
    };
    result.Activities.Add(write);
 
    var delay = new Delay()
    {
        Duration = TimeSpan.FromSeconds(5)
    };
    result.Activities.Add(delay);
 
    var request2 = new Send()
    {
        OperationName = "Operation2",
        Endpoint = endpoint,
        AdditionalCorrelations =
        {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        ServiceContractName = ns + "MyService",
        Value = new InArgument<Person>(person)
    };
    result.Activities.Add(request2);
 
 
    var reply2 = new ReceiveReply()
    {
        Request = request2,
        Value = new OutArgument<string>(response)
    };
    result.Activities.Add(reply2);
 
    var write2 = new WriteLine()
    {
        Text = new InArgument<string>(response)
    };
    result.Activities.Add(write2);
 
 
    return result;
}

 

Conclusion.

The WF3/WCF marriage in Wf3.5 left quite a bit to be desired and fortunately live has become a lot simpler with WF4.

 

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , , , , ,

Ik heb zojuist een nieuwe versie van .NET Events in Nederland beschikbaar gemaakt.

 

Nieuw is de mogelijkheid om de gegevens ook als iCalendar agenda binnen te halen in Microsoft Outlook of een andere agenda die met een iCalendar feed om kan gaan. Wel zo handig om te zien of er conflicten zijn met andere afspraken als je naar een bijeenkomst wil gaan.

Hoe het een en ander in 4 eenvoudige stappen in Outlook 2007 moet configureren staat hier.

 

En zoals altijd, weet je een bijeenkomst die voor .NET ontwikkelaars interessant is maar die nog niet op de lijst staat? Geef hem dan even door.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 1 comment(s)
Filed under:

In the previous two blog posts (here and here) I showed how to create and expose a Windows Workflow Foundation 4 workflow via WCF and have both a workflow and a regular C# client work with it. But the parameter and return value where real simple with just a string each. So how about passing some more complex data.

 

To show how to do so I will replace the singe string with a person object. Admittedly not the most complex person with only an id, first and last name and their birth date but enough to demonstrate the principal.

 

The main of the service, which uses a WorkflowServiceHost, stays exactly the same so see the first blog post for that part. The CreateWorkflow() function will change now reflecting the fact that the service will no longer expect a string but a person object.

private static WorkflowElement CreateWorkflow()
{
    var result = new Sequence();
    var input = new Variable<Person>();
    result.Variables.Add(input);
    XNamespace ns = "http://tempuri.org";
 
    var handle = new Variable<CorrelationHandle>();
    result.Variables.Add(handle);
 
    var receive = new Receive()
    {
        OperationName = "Operation1",
        ServiceContractName = ns + "MyService",
        Value = new OutArgument<Person>(input),
        AdditionalCorrelations = {                   
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        CanCreateInstance = true
    };
    result.Activities.Add(receive);
 
    var write = new WriteLine()
    {
        Text = new InArgument<string>(env => string.Format("\tThe workflow was called with '{0}'.", input.Get(env)))
    };
    result.Activities.Add(write);
 
    var reply = new SendReply()
    {
        Request = receive,
        Value = new InArgument<string>("The result")
    };
    result.Activities.Add(reply);
 
    return result;
}

Not a big change really as we only have to change the input variable from Variable<string> to Variable<Person> and do the same with the Value property of the Receive activity which goes from new OutArgument<string>(input) to new OutArgument<Person>(input).

 

That leaves the Person class and this is just straightforward WCF work by turning the basic class into a data contract.

[DataContract(Namespace = "urn:WF4Sample:person")]
public class Person
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string FirstName { get; set; }
    [DataMember]
    public string LastName { get; set; }
    [DataMember]
    public DateTime BirthDate { get; set; }
 
    public override string ToString()
    {
        return string.Format("{0} {1} was born on {2} (ID: {3})", FirstName, LastName, BirthDate, Id);
    }
}

Again no big deal Smile

 

So with the service done we need to make the same changes to the two client applications. First the workflow client using the Send activity.

The Person class is exactly the same as on the server so just copy the code as is. Again the main in the workflow client doesn’t change, see the first blog post for that part. The part that does change a little is the CreateWorkflow() function so it creates and sends a Person instead of a string. The code goes as follows:

private static WorkflowElement CreateWorkflow()
{
    var result = new Sequence();
    XNamespace ns = "http://tempuri.org";
 
    var endpoint = new Endpoint()
    {
        Uri = new Uri("http://localhost:8090/Sequence1/Operation1"),
        Binding = new BasicHttpBinding()
    };
 
    var response = new Variable<string>();
    result.Variables.Add(response);
 
    var handle = new Variable<CorrelationHandle>();
    result.Variables.Add(handle);
 
    var person = new Person()
    {
        Id = 7,
        FirstName = "Maurice",
        LastName = "de Beijer",
        BirthDate = new DateTime(1962, 8, 24)
    };
 
    var request = new Send()
    {
        OperationName = "Operation1",
        Endpoint = endpoint,
        AdditionalCorrelations =
        {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        ServiceContractName = ns + "MyService",
        Value = new InArgument<Person>(person)
    };
    result.Activities.Add(request);
 
 
    var reply = new ReceiveReply()
    {
        Request = request,
        Value = new OutArgument<string>(response)
    };
    result.Activities.Add(reply);
 
    var write = new WriteLine()
    {
        Text = new InArgument<string>(response)
    };
    result.Activities.Add(write);
 
    return result;
}

Again a very minor change.

 

So how about the C# console client from the second blog post?

The service contract changes a little and is now:

[ServiceContract]
interface MyService
{
    [OperationContract]
    Operation1Response Operation1(Person request);
}

 

The person class is very different from the service though and looks like this:

[MessageContract(WrapperNamespace = "urn:WF4Sample:person")]
internal class Person
{
    [MessageBodyMember(Namespace = "urn:WF4Sample:person")]
    public int Id { get; set; }
    [MessageBodyMember(Namespace = "urn:WF4Sample:person")]
    public string FirstName { get; set; }
    [MessageBodyMember(Namespace = "urn:WF4Sample:person")]
    public string LastName { get; set; }
    [MessageBodyMember(Namespace = "urn:WF4Sample:person")]
    public DateTime BirthDate { get; set; }
}

But besides the difference from using a message contract as opposed to a data contract this isn’t a big deal.

The main program changes a bit but not a whole lot. Again the only changes are due to the fact we are passing in a Person instead of a string.

static void Main(string[] args)
{
    var binding = new BasicHttpBinding();
    var endpoint = new EndpointAddress("http://localhost:8090/Sequence1/Operation1");
    var factory = new ChannelFactory<MyService>(binding, endpoint);
    var proxy = factory.CreateChannel();
 
    var request = new Person()
    {
        Id = 23,
        FirstName = "Joe",
        LastName = "Regular",
        BirthDate = new DateTime(1975, 7, 1)
    };
    var result = proxy.Operation1(request);
    Console.WriteLine(result.Value);
 
    Console.WriteLine("Regular client is done.");
    Console.ReadLine();
}

 

All together not bad.

 

But so far we have only used a very simple workflow with a single message send to it. Suppose you want to send multiple messages to the same workflow? Well in that case we need to use message correlation as we will see next time.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 5 comment(s)

In a previous blog post I described how to use the WorkflowServiceHost and host a workflow with a Receive activity that waits for WCF messages. I also added a WF4 client that called the service and received a response. However a lot of clients out there are not going to be workflows but “regular” code that calls into out workflow. So what does it take to have a simple console application talk to our WF4 service?

 

The first thing we need is an service contract describing the operation we can call. In our workflow the operation is named “Operation1” and our service contact name was “MyService” with the default namespace of “http://tempuri.org”. So our service contract looks like this:

[ServiceContract]
interface MyService
{
    [OperationContract]
    Operation1Response Operation1(Operation1Request request);
}

 

Next up are the request and response objects. In this case I am using just two strings so they are pretty simple as well.

[MessageContract(IsWrapped = false)]
public class Operation1Request
{
    [MessageBodyMember(
        Namespace = "http://schemas.microsoft.com/2003/10/Serialization/",
        Name = "string")]
    public string Value { get; set; }
}
 
[MessageContract(IsWrapped = false)]
public class Operation1Response
{
    [MessageBodyMember(
        Namespace = "http://schemas.microsoft.com/2003/10/Serialization/",
        Name = "string")]
    public string Value { get; set; }
}

The interesting to note here is that I have to use a MessageContact instead of a DataContract and I have to make sure the data isn’t wrapped in an extra element. Also interesting is the fact that the data is send across using the name “string”, not the easiest C# identifier to work with so I called them “Value” instead.

 

Using these WCF definitions creating a simple console app that calls the workflow is a breeze.

static void Main(string[] args)
{
    var binding = new BasicHttpBinding();
    var endpoint = new EndpointAddress("http://localhost:8090/Sequence1/Operation1");
    var factory = new ChannelFactory<MyService>(binding, endpoint);
    var proxy = factory.CreateChannel();
 
    var request = new Operation1Request()
    {
        Value = "Test from a console."
    };
    var result = proxy.Operation1(request);
 
    Console.WriteLine(result.Value);
 
    Console.WriteLine("Regular client is done.");
    Console.ReadLine();
}

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , , , , ,

Michael Kennedy, one of the co instructors from DevelopMentor, has created a combined feed from all the blogs of the various DevelopMentor. An awesome group of people with a lot of knowledge to share. Highly recommended in you RSS reader.

http://feeds.feedburner.com/DevelopmentorInstructors

 

If you want to check out a list of the individual blog feeds look here.

 

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Recently I started using ASP.NET MVC for a real project. SO far I had been reluctant to use ASP.NET MVC as it meant giving up on the post back model that so many of the available ASP.NET controls depend on. Yet a lot of people I know and respect where saying good things about ASP.NET MVC so I decided to try it out on a small but real project.

 

Well the project isn’t finished yet but I am a convert Smile

Using ASP.NET MVC is much nicer that I expected it to be. Sure the post back model, and with it a lot of controls, are gone but I started doing more client side stuff using jQuery and the many plug-ins available. And even where not doing a lot of client side work ASP.NET MVC makes live quite easy.

The main difference is that ASP.NET MVC lends itself very well to test driven development. Now I not a pure TDD zealot but covering a lot of the code with unit test, regardless if you create the test first or later, is pretty much a requirement of a good software project. And ASP.NET MVC lend itself much better to unit testing than ASP.NET web forms.

ASP.NET MVC will not be the thing for everyone. If you are heavy into designers and drag-drop development web forms will be a better match for you. But if you are like me and like working with code ASP.NET MVC is a joy to work with.

The book I keep by my side is Steven Sanderson’s Pro ASP.NET MVC Framework. It has been very valuable, both on giving me a behind the scenes insight and solving some practical issues I ran into.

 

Recommended.

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 2 comment(s)
Filed under: , ,

I am working on a new ASP.NET MVC app and like so many other applications out there I need to log any errors should they occur. recently I heard about ELMAH Error Logging Modules and Handlers for ASP.NET through this blog post of Scott Hanselman. So as using en existing and proven solution is always better that reinventing the wheel I decided to try ELMAH and see how easy it is to get started.

 

Well I can only say I am positively amazed at how easy it is.

homeshot[1]

 

Starting from scratch it took me less that 2 hours to download and integrate it into my current ASP.NET MVC application and log all errors to SQL Server. That is including security so not everyone can check the error log.

 

There are lots of additional capabilities I haven’t looked at yet like setting up an RSS feed, emailing or even tweeting errors messages. But judging from the experience so far that should prove easy enough.

 

My only regret is that I didn’t check it out before.

 

Checkout the project run by Atif Aziz here.

 

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

Posted by Maurice | 1 comment(s)
Filed under: , , ,

There are several ways you can go about it if you want to post a tweet when you can upload a photo to Flickr. The official Flickr way is add Twitter as a blog in your account “Extending Flickr” page and clicking “Blog this” on a new picture you want to tweet.

While this works perfectly well there is also an automatic way if using the Flickr REST API. One of the options is to generate an RSS feed. Using this and the free TwitterFeed service I can automatically tweet whenever I add a new image to Flickr.

The Flickr FSS feed looks like this: http://api.flickr.com/services/feeds/photos_public.gne?id=[[your flickr user id]]&format=rss_200.

Nice and simple. And there are additional option of the RSS feed to filter only images with a specific tag if you don’t want to auto twitter al your new images.

Enjoy!

Posted by Maurice | with no comments
More Posts Next page »