The Problem Solver

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

Workflow

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 | 7 comment(s)
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: , ,

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.

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: , , , , ,

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

There are several ways to use WCF in combination with Windows Workflow Foundation 4. The two can be combined inside of a XAMLX file as Ron Jacobs describes here.

Another option is using the WF4 Receive and SendReply activities and hosting the workflow yourself using a WorkflowServiceHost. This is quite a useful option but, at least at the moment, not quite straightforward. When I was trying to get things working using a declarative workflow and the designer Visual Studio 2010 would keep in locking up so all workflows in this example will be coded using C#. Not quite the way things are supposed to be done but it does give some useful insight.

 

Creating a workflow to handle requests

The basic activity we need on the service side is the Receive activity. This allows us to wait for incoming requests and process them, either by using a new workflow or an existing one. If we want to send a response we need to use a SendReply activity as well, if not we are creating a one way contract.

Note that there is not going to be a formal contract as we normally have in WCF. Instead we configure the Receive and SendReply and a contract is derived from that. Not quite sure if I like that as a service contract is supposed to be very much locked down.

The code for the service workflow looks like this:

private static WorkflowElement CreateWorkflow()
{
    var result = new Sequence();
    var input = new Variable<string>();
    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<string>(input),
        AdditionalCorrelations = {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        CanCreateInstance = true
    };
 
    result.Activities.Add(receive);
 
    var write = new WriteLine()
    {
        Text = new InArgument<string>(env => string.Format("The 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;
}

 

For the most part quite straightforward. We receive a message as defined by the Receive activity Value property, in this case just a string. The message is printed and we return another string through the Value property of the SendReply. Using C# code really shows where we need to use arguments and variables, something that is somewhat hidden when using XAML.

One thing I don’t like is having to create and add the CorrelationHandle. It might be needed internally but I don’t really want to be bothered. Setting the Request property on the SendReply to hook the two together should be enough. Lets hope this disappears before WF4 ships.

 

Hosting the service workflow

With the service workflow in place we need to create a WorkflowServiceHost and actually make the workflow available. The WorkflowServiceHost  is configured using a Service definition which in turn contains a WorkflowServiceImplementation containing the actual workflow and a WCF endpoint. The hosting code looks like this:

XNamespace ns = "http://tempuri.org";
 
var service = new Service();
service.Implementation = new WorkflowServiceImplementation()
{
    Name = ns + "MyService",
    Body = CreateWorkflow()
};
 
service.Endpoints.Add(new Endpoint()
{
    Uri = new Uri("Operation1", UriKind.Relative),
    Binding = new BasicHttpBinding(),
    ServiceContractName = ns + "MyService"
});
 
var host = new WorkflowServiceHost(
    service,
    new Uri("http://localhost:8090/Sequence1"));
 
host.Open();
 
Console.WriteLine("Press enter to stop");
Console.ReadLine();
 
host.Close();

 

The main point here is making sure the Name and ServiceContractName, both for the Endpoint and Receive, are configured correctly or the WorkflowServiceHost will not be able to start and you will get a System.InvalidOperationException with a message something like “Cannot add endpoint because ContractDescription with Name='MyService' and Namespace='http://tempuri.org' can not be found.”.

 

Creating a calling workflow

With the service side in place we now need to create a client workflow to call into our service. In this case the main activity to use is the Send which sends the request to the WCF service and the ReceiveReply which waits for the response to arrive. The whole is very similar to the service workflow and again we need to use the annoying CorrelationHandle. The workflow code looks like this:

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 request = new Send()
    {
        OperationName = "Operation1",
        Endpoint = endpoint,
        AdditionalCorrelations =
        {
           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}
        },
        ServiceContractName = ns + "MyService",
        Value = new InArgument<string>("Test")
    };
    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;
}

Just like any other WCF service the main point is to make sure that the service contract names, namespaces and data types match or message will not be processed correctly (or at all). Running this workflow has no special hosting requirements so using the WorkflowInvoker will do just fine.

var workflow = CreateWorkflow();
WorkflowInvoker.Invoke(workflow);
 
Console.WriteLine("Client Done.");
Console.ReadLine();

 

Conclusion

Using WCF and WF4 together is not hard. Unfortunately the designer seems to be letting me down but fortunately everything we can do using the designer can also be done using code.

 

Enjoy!

 

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

 

One thing that has completely changed in Windows Workflow Foundation is the way we work with data in a workflow. In WF 3 we used properties to store data. We could use regular .NET properties but most of the time dependency properties where the smarter choice. Dependency properties left the way data was stored to be handled by the workflow runtime but in our program we could use them just like any other property. And the big bonus was we could use property binding to tie different properties on different activities together in any way we saw fit. These dependency properties where much like the dependency properties found in WPF although there was no common base class or interface used.

 

In WF 4 this has all changed!

We are no longer using properties to store data. Most of the time we will need to use variables, represented by types deriving from Variable, or arguments, types deriving from Argument, to work with data. These arguments and variables don’t actually store the data, they just describe the data and let you get at it. The actual data is stored somewhere inside of the workflow using a LocationEnvironment. To store some data inside of your workflow we need to add a Variable to the workflow or one of the nested activities. The basic rule is to move it as close as possible to the activities that need it at a place there all activities that need access to the data can find the value in the path to the root activity.

The root path is a big difference from WF3 where any activity could get to any other piece of data anywhere in the workflow. This makes for some hard to maintain workflows and means that persistence always has to store everything. With the new model you are much restricted in where you can store data but it makes for a much cleaner, and therefore faster, persistence model.

 

Take the following class:

public class Customer
{
    public Customer(string name, CreditRating creditRating)
    {
        Name = name;
        CreditRating = creditRating;
    }
 
    public string Name { get; set; }
    public CreditRating CreditRating { get; set; }
}
 
public enum CreditRating
{
    Good,
    Average,
    Poor
}

 

Now I want to create a workflow that prints the customer with its credit rating, updates the credit rating and prints the data gain. Not very complex but enough to display the steps.

The workflow looks like this:

image

Because all three activities need to use the same customer object we need to store it at a shared parent. In this case the workflow is very simple and the shared parent is the root Sequence itself.

Adding new variable is done using the variables menu at the left bottom of the designer.

image

The Name is used throughout the workflow to store and retrieve the data. The Default can be left empty but in this case I create a new Customer object my entering the following expression:

New WorkflowConsoleApplication4.Customer("Maurice", WorkflowConsoleApplication4.CreditRating.Average)

Selecting the data type is done using the .NET type selector dialog shown below. Personally I find the type selector not the easiest to work with but it gets the job done.

image

 

The text to print in the WriteLine activities is quite straightforward and you get full IntelliSense, even on our own types. The same is true for the Assign activity.

String.Format("Customer '{0}' has a credit rating of {1}.", theCustomer.Name, theCustomer.CreditRating)

image

 

Not bad and easy enough to do.

 

Creating the workflow using code

Every workflow you create using the designer you can also create using code so what would this same workflow look like when done using pure code?

var theCustomer = new Variable<Customer>()
{
    Default = new Customer("Maurice de Beijer", CreditRating.Average)
};
 
var result = new Sequence();
result.Variables.Add(theCustomer);
 
result.Activities.Add(new WriteLine()
{
    Text = new InArgument<string>(env => string.Format("Customer '{0}' has a credit rating of {1}.",
        theCustomer.Get(env).Name,
        theCustomer.Get(env).CreditRating))
});
 
result.Activities.Add(new Assign<CreditRating>()
{
    To = new OutArgument<CreditRating>(env => theCustomer.Get(env).CreditRating),
    Value = CreditRating.Good
});
 
result.Activities.Add(new WriteLine()
{
    Text = new InArgument<string>(env => string.Format("Customer '{0}' has a credit rating of {1}.",
        theCustomer.Get(env).Name,
        theCustomer.Get(env).CreditRating))
});

 

The same WriteLine activity with the simple String.Format() expression now looks quite a bit different. And not different in a good sort of way Sad

Instead of using “theCustomer.Name” as we could in the designer now we have to use the expression “theCustomer.Get(env).Name” and instead of just typing the complete expression we have to wrap it in an InArgument<string>. Not very pretty but it does show is what is going on under the covers so lets take a closer look.

The WriteLine activity needs something to print and this is input for the activity as it never modifies it. So we need to use an InArgument. In the case of the Assign activity it needs change some data so it needs to use an OutArgument. The third option, not used here, is an InOutArgument which lets an activity read and modify some data.

The InArgument has several constructors, in the case I am using one that takes an expression with a LocationEnvironment which we can use to retrieve the actual customer object.

So where is this customer stored?

Just like in the designer we need to create a variable. This is done at the top using this code:

 

var result = new Sequence();
var theCustomer = new Variable<Customer>()
{
    Default = new Customer("Maurice de Beijer", CreditRating.Average)
};
result.Variables.Add(theCustomer);

 

So all in all this is very different in WF4 as compared to WF3. The code use to create a workflow doesn’t look very pretty, but then again the code use to create and link dependency properties didn’t either. Fortunately the designer hides most of the complexity for us by just letting us enter the expression we want and not bother us with the way data is actually handled inside of a workflow.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

In my previous post I showed how to use an AsyncOperationBlock to do some basic asynchronous work in WF 4. As I mentioned this was for relatively short lived asynchronous operations as the workflow could not be persisted and unloaded while this asynchronous work was executing.

So how about real long running work, the kind you run into with real business application like waiting for an invoice to be paid?

 

With these king of jobs an AsyncOperationBlock will not work because you don’t want to keep the workflow in memory all the time.

So what do we need to use here?

Well the basic building block is called a Bookmark.

 

A bookmark is just a way of saying to the workflow: give a pointer to where you are and I will tell you later to resume from this pointer.

 

Creating a bookmark is easy. In the execute method of our activity we call the CreateNamedBookmark() function of the ActivityExecutionContext passed to us with the name of the bookmark. The bookmark name is no longer an IComparable but just a regular string.

Whenever we are done with our job we can use the WorkflowInstance to call ResumeBookmark() passing in the name of the bookmark and some data. The activity will be signaled as closed and we are ready to go.

One big difference is the activity base class. In the previous sample I was using the CodeActivity as the baseclass for my custom activity while in this case I am using the more powerful NativeActivity<T>.

A word of warning here, these are the names in WF4 Beta 1 and they are likely to change before WF4 is release!

 

So what does my activity look like?

class AsyncWorker : NativeActivity<String>
{
    protected override void Execute(ActivityExecutionContext context)
    {
        Console.WriteLine("CreateNamedBookmark on thread\t{0}", Thread.CurrentThread.ManagedThreadId);
        context.CreateNamedBookmark("bm");
    }
}

It prints a bit of status info and most importantly it creates a new named bookmark called “bm”.

 

The main program looks like this:

var workflow = CreateWorkflow();
var instance = new WorkflowInstance(workflow);
instance.OnIdle = () =>
{
    Console.WriteLine("Idle on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);
    return IdleAction.Nothing;
};
instance.OnCompleted = (e) => { Console.WriteLine("Completed on thread\t\t{0}", Thread.CurrentThread.ManagedThreadId); };
 
instance.Run();
 
Console.WriteLine("Sleep on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
 
Console.WriteLine("ResumeBookmark on thread\t{0}", Thread.CurrentThread.ManagedThreadId);
instance.ResumeBookmark("bm", null);
 
Console.WriteLine("Sleep on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
 
Console.WriteLine("Done on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);
Console.ReadLine();

If you strip out the printing of the status info you are basically left with:

var workflow = CreateWorkflow();
var instance = new WorkflowInstance(workflow);
instance.Run();
instance.ResumeBookmark("bm", null);

 

One interesting thing I noted is that is appears to be ok to resume a bookmark before it is created.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

The whole asynchronous execution model in WF 4 has changed quite a bit from WF 3. Not really a surprise as this one of the areas where WF 3 was really hard to work with.

 

Below is a very simple example. It does nothing useful, it’s only task is to show how to get started with asynchronous work in WF 4.

class AsyncWorker : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
        var asyncContext = context.SetupAsyncOperationBlock();
        var task = new Task(DoAsyncWork, asyncContext);
        task.Start();
    }
 
    private static void DoAsyncWork(object state)
    {
        var asyncContext = (AsyncOperationContext)state;
 
        Console.WriteLine("Doing some work.");
        Thread.Sleep(5000);
 
        asyncContext.CompleteOperation((ctx, bm, s) => { }, null);
    }
}

 

The important parts here are:

  • CodeActivityContext.SetupAsyncOperationBlock()
    This basically tells the runtime that the activity is going to do something asynchronously and that the runtime should not continue with the next activity when the Execute() is finished. It also means that the workflow cannot be persisted until this operation is complete.
  • AsyncOperationContext.CompleteOperation()
    This tells the workflow runtime that the operation is complete and that the next activity can be scheduled or the workflow may be persisted.

Because the workflow can’t be persisted this is designed for relatively short running operations. So something like saving some data via a WCF service. If you need the sort of asynchronous work where you might be waiting for day’s on an end user this is not the way to go. In that case you should be looking at bookmarks instead.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

 

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

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

 

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

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

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 Sad

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.

image

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 Sad

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

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

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

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 Sad

 

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

image

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:

image

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.

image

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.

image

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.

image

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 Sad

In the next post more about how the workflow is created and started at runtime.

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

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.

Download the PowerPoint and samples

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

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.

Download the PowerPoint and samples

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

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.

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

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

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

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