The Problem Solver

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

Google Ads

This Blog

Syndication

Search

Tags

News





  • View Maurice De Beijer's profile on LinkedIn

Community

Email Notifications

Explore

Archives

December 2009 - Posts

Note: This blog post is written using the .NET framework 4.0 Beta 2

With Windows Workflow Foundation 3 it was possible to rehost the workflow designer in your own application. But possible is about all there was to say about it as it was pretty hard to do anything beyond the basics.

 

With Windows Workflow Foundation 4 live has become much better on the rehosting front Smile In fact it is possible to create the fully functional and useful workflow editor below in about 200 lines of code. Now that is more like it!

image

 

The WorkflowDesigner

The WorkflowDesigner is the main class to work with. This exposes the actual design surface through the View property and the linked property sheet through the PropertyInspectorView property. Both these properties point to ready to use WPF UIElement’s so, as long as the host used WPF, adding them to a form is easy. And loading or saving a workflow is easy to, all it takes is a Load() and Save() function pointing to a XAML file. The code that does this part is below:

_workflowDesigner = new WorkflowDesigner();
_workflowDesigner.Load(_fileName);
 
var view = _workflowDesigner.View;
Grid.SetColumn(view, 1);
Grid.SetRow(view, 1);
LayoutGrid.Children.Add(view);
 
var propInspector = _workflowDesigner.PropertyInspectorView;
Grid.SetColumn(propInspector, 2);
Grid.SetRow(propInspector, 1);
LayoutGrid.Children.Add(propInspector);

 

DesignerMetadata

One thing that is needed is to register the workflow activity designer metadata. It is just a single call but leaving it out means all activities are just a small collapsed shape and won’t expand.

new DesignerMetadata().Register();

 

Displaying the toolbox with activities

The toolbox on the left if another standard WPF control called the ToolboxControl. Again easy to add to any WPF form. Add to it ToolboxItemWrapper with the activity type to add and you are good to go. Dragging controls onto the design surface just works, no extra work required. In the code below I just scan through all types in a few assemblies and if they are valid activities I add them to the toolbox. Again nice and easy.

var toolbox = new ToolboxControl();
var cat = new ToolboxCategory("Standard Activities");
var assemblies = new List<Assembly>();
assemblies.Add(typeof(Send).Assembly);
assemblies.Add(typeof(Delay).Assembly);
assemblies.Add(typeof(ReceiveAndSendReplyFactory).Assembly);
 
var query = from asm in assemblies
            from type in asm.GetTypes()
            where type.IsPublic &&
            !type.IsNested &&
            !type.IsAbstract &&
            !type.ContainsGenericParameters &&
            (typeof(Activity).IsAssignableFrom(type) ||
            typeof(IActivityTemplateFactory).IsAssignableFrom(type))
            orderby type.Name
            select new ToolboxItemWrapper(type);
 
query.ToList().ForEach(ti => cat.Add(ti));
toolbox.Categories.Add(cat);
Grid.SetColumn(toolbox, 0);
Grid.SetRow(toolbox, 1);
LayoutGrid.Children.Add(toolbox);

 

The current selection

At the top of the form I display the currently selected activity and its parents. The WorkflowDesigner has an Items collection with a bunch of useful objects in there. One of these is a Selection object. We could periodically check this Selection but its even easier to subscribe to chances using the Subscribe() function and passing in a handler that is called whenever the selection changes. Setting up the subscription is another one-liner.

_workflowDesigner.Context.Items.Subscribe<Selection>(SelectionChanged);

The handler itself isn’t very complex either.

private void SelectionChanged(Selection selection)
{
    var modelItem = selection.PrimarySelection;
    var sb = new StringBuilder();
 
    while (modelItem != null)
    {
        var displayName = modelItem.Properties["DisplayName"];
 
        if (displayName != null)
        {
            if (sb.Length > 0)
                sb.Insert(0, " - ");
            sb.Insert(0, displayName.ComputedValue);
        }
 
        modelItem = modelItem.Parent;
    }
 
    CurrentActivityName.Text = sb.ToString();
}

 

Validating the workflow

It would be nice to let the user know if the workflow is valid. Again quite easy to do by adding a IValidationErrorService to the WorkflowDesigner services collection. In this case I added a listbox to the form and let the IValidationErrorService add each error to the ListBox items. No need to call anything, the IValidationErrorService is automatically called as soon as anything in the workflow changes.

var validationErrorService = new ValidationErrorService(WorkflowErrors.Items);
_workflowDesigner.Context.Services.Publish<IValidationErrorService>(validationErrorService);

The IValidationErrorService consists of a single function that gets the list of errors as a parameter.

public class ValidationErrorService : IValidationErrorService
{
    private IList _errorList;
    public ValidationErrorService(IList errorList)
    {
        _errorList = errorList;
    }
 
    public void ShowValidationErrors(IList<ValidationErrorInfo> errors)
    {
        _errorList.Clear();
        foreach (var error in errors)
        {
            _errorList.Add(error.Message);
        }
    }
}

 

Running the workflow

Just to be able to run the workflow I added a bit of code that uses a WorkflowApplication to run the workflow. Loading it is also easy, just call the ActivityXamlServices.Load() passing in the file and it will return a DynamicActivity ready to run.

var writer = new StringWriter();
var workflow = ActivityXamlServices.Load(_fileName);
var wa = new WorkflowApplication(workflow);
wa.Extensions.Add(writer);
wa.Completed = WorkflowCompleted;
wa.OnUnhandledException = WorkflowUnhandledException;
wa.Run();

 

So all together I have a working application that I can use to edit and run workflows. So who still needs Visual Studio 2010?

Sweet Smile

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

One of the things that wasn’t possible in Windows Workflow Foundation 3 was flowing transaction over a WCF service request into a workflow. We could have transactions on the client, we could have transactions on the server but they would not be able to cooperate.

Fortunately that is one the problems that Windows Workflow Foundation 4 solves for us. [:)]

 

In WF4 we can use the ReceiveAndSendreply template to configure a workflow as a service and accept WCF calls. That works just fine but doesn’t allow for transactions. If we want to use transactions we need to add the TransactedReceiveScope to the workflow. Move the ReceiveRequest activity into the Request part and the SendResponse activity into the Body part. Don’t forget the last part because leaving the SendResponse below the TransactedReceiveScope means things will not work and the error message will be less that helpful [:(]

 

image

This is enough to make all work done during the message receive part of a transaction. To show what is happening with transactions I am using the following code to record and return the transactional status:

using System.Text;
using System.Transactions;
 
public class TransactionReport
{
    public static string GetReport()
    {
        var sb = new StringBuilder();
 
        if (Transaction.Current == null)
        {
            sb.Append("No transaction");
        }
        else
        {
            sb.AppendFormat("TransactionInformation\n");
            sb.AppendFormat("\tLocalIdentifier {0}\n", Transaction.Current.TransactionInformation.LocalIdentifier);
            sb.AppendFormat("\tDistributedIdentifier {0}\n", Transaction.Current.TransactionInformation.DistributedIdentifier);
            sb.AppendFormat("\tStatus {0}\n", Transaction.Current.TransactionInformation.Status);
            sb.AppendFormat("\tIsolationLevel {0}\n", Transaction.Current.IsolationLevel);
        }
 
        return sb.ToString();
    }
}

 

Adding a console application with a service reference with the following code:

using System;
using Client.ServiceReference1;
 
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            var proxy = new ServiceClient();
            var result = proxy.PostData();
 
            Console.WriteLine(result);
 
 
            Console.ReadLine();
        }
    }
}

Produces the following output:

image

No big surprises there. The only thing I am missing is the ability to control the IsolationLevel which seems to be fixed at Serializable.

 

 

Flowing transactions from the client

The first step we need to take for client transactions to flow to the service is do the service call inside of a transaction using this client code:

using System;
using System.Transactions;
using Client.ServiceReference1;
 
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
 
            using (var tx = new TransactionScope())
            {
                Console.WriteLine(TransactionReport.GetReport());
 
                var proxy = new ServiceClient();
                var result = proxy.PostData();
 
                Console.WriteLine(result);
 
                Console.WriteLine(TransactionReport.GetReport());
            }
 
            Console.ReadLine();
        }
    }
}

If we run the code now we get the following output:

image

The transaction is there on the client but DistributedIdentifier is still empty so the transaction doesn’t flow to the service yet.

 

Before transactions will flow we need to do two things. First of all we need a WCF binding that can flow transactions. So far we have been using the BasicHttpBinding, the default for an HTTP endpoint in the .NET 4 default configuration, which doesn’t support transactions. So we need to switch to something like WSHttpBinding that does. Next we need to enable transaction flow because it is disabled by default. All of this can be done by adding a little extra configuration to the web.config file.

<bindings>
  <wsHttpBinding>
    <binding transactionFlow="true"/>
  </wsHttpBinding>
</bindings>
<services>
  <service name="Service1">
    <endpoint address=""
              binding="wsHttpBinding"
              contract="IService" />
  </service>
</services>

With this in place all we need to do is update the client service reference and transactions will flow from the client to the service.

image

 

Keep in mind that flowing transactions across a network boundary is a very powerful but also a very dangerous concept. You might be letting someone else, from a different organization, place locks on your database. So make sure you understand the implications of flowing transactions before you open up a whole can of worms!

 

Nice [:)]

 

Enjoy!

TheProblemSolver
DotNetEvents

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

The recordings from my recent online Windows Workflow Foundation 4 presentation have been posted online. Get them while they are hot Smile

 

You can download the samples and PowerPoint from here.

 

Streaming:

http://developmentor.s3.amazonaws.com/webcasts/Meet-The-New-Workflow-WF4-Maurice-de-Beijer-2009-12-01.wvx

Video Download:

http://developmentor.s3.amazonaws.com/webcasts/Meet-The-New-Workflow-WF4-Maurice-de-Beijer-2009-12-01.zip

Video As WMV (used by streaming link):
http://developmentor.s3.amazonaws.com/webcasts/Meet-The-New-Workflow-WF4-Maurice-de-Beijer-2009-12-01.wmv

MP3 (iTunes Ready):
http://developmentor.s3.amazonaws.com/webcasts/Meet-The-New-Workflow-WF4-Maurice-de-Beijer-2009-12-01.mp3

Download MP3 (iTunes Ready):

http://developmentor.s3.amazonaws.com/webcasts/Meet-The-New-Workflow-WF4-Maurice-de-Beijer-2009-12-01-mp3only.zip

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

Note: This blog post is written using the .NET framework 4.0 Beta 2

 

In my previous post about Windows Workflow Foundation 4 I used the CorrelationScope activity to arrange for the activity correlation between a Send and the related ReceiveReply activities. That is quite easy to do and personally I find things nice and easy to arrange with both messaging activities nested in the CorrelationScope activity . The default templates use a slightly different approach though and use a RequestReplyCorrelationInitializer. The end result is the same though so just use what you think best.

 

Just for comparison below is the code to create the same workflow with a RequestReplyCorrelationInitializer.

static Activity CreateWorkflow()
{
    var getDataResult = new Variable<string>();
    var handel = new Variable<CorrelationHandle>();
 
    var send = new Send()
    {
        CorrelationInitializers =
        {
            new RequestReplyCorrelationInitializer()
            { 
                CorrelationHandle = handel
            }
        },
        OperationName = "GetData",
        ServiceContractName = "IService1",
        Endpoint = new Endpoint()
        {
            Binding = new BasicHttpBinding(),
            AddressUri = new Uri("http://localhost:8080/GetDataService/")
        },
        Content = new SendParametersContent()
        {
            Parameters = 
            {
                {"value", new InArgument<int>(42)}
            }
        }
    };
 
    var receive = new ReceiveReply()
    {
        Request = send,
        Content = new ReceiveParametersContent
        {
            Parameters =
            {
                {"GetDataResult", new OutArgument<string>(getDataResult)}
            }
        }
    };
 
    var workflow = new Sequence()
    {
        Variables = { getDataResult, handel },
        Activities = 
        {
            send, 
            receive,
            new WriteLine() { Text = getDataResult } 
        }
    };
 
    return workflow;
}

 

The workflow is even a little simpler because there are fewer activities involved. And this is effectively the same as the SendAndReceiveReply and ReceiveAndSendReply templates generate in WF4.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

Note: This blog post is written using the .NET framework 4.0 Beta 2

There are two types of correlation to think about in Windows Workflow Foundation:

  • Message correlation
    Basically sending multiple requests to the same workflow.
  • Activity correlation
    Making sure two activities work together.

In this post I am going to show activity correlation

 

Activity correlation is used when multiple activities form a single logical action. Think about the Send and ReceiveReply activities. Two distinct activities but they work together because the first, the Send activity, send a WCF request somewhere and the second, the ReceiveReply activity, receives the response. The same goes when receiving messages.

The good thing is that when we use the ReceiveAndSendReply or SendAndReceiveReply templates everything will be done for us and the required activity correlation will be configured automatically. However sometimes we might need to do so ourselves because we create a workflow in a different way, like using regular code.

 

So when we start using the Send and ReceiveReply or the Receive and SendReply activities from code we need to do a bit more work. How much varies and depends on both the workflow and the hosting environment we use. The big exception to the rule is when we use the WorkflowServiceHost and no parallel or overlapping messaging activities. In that case life is simple and the WorkflowServiceHost takes care of all required correlation for us. Nice Smile

That does leave for quite a few scenarios where we do need to add activity correlation though. The obvious one is using the WorkflowApplication or WorkflowInvoker to run our workflows. Fortunately this produces an System.InvalidOperationException with a message like:

The Send activity is configured with a request/reply Operation 'GetData', however, there is no ReceiveReply activity paired with it. Please pair Send with ReceiveReply and correlate them using a CorrelationHandle.

There are different ways to configure activity correlation but the easiest way is to wrap the activity pair in a CorrelationScope activity. The CorrelationScope will automatically create the required correlation handle and the messaging activities will use this handle.

 

Show me the code

The code below calls a very standard and basic WCF service passing in the value if 42 and prints the result. The important part is that the send are receive actions are done inside of a CorrelationScope activity so this workflow can run in any host.

static Activity CreateWorkflow()
{
    var getDataResult = new Variable<string>();
 
    var send = new Send()
    {
        OperationName = "GetData",
        ServiceContractName = "IService1",
        Endpoint = new Endpoint()
        {
            Binding = new BasicHttpBinding(),
            AddressUri = new Uri("http://localhost:8080/GetDataService/")
        },
        Content = new SendParametersContent()
        {
            Parameters = 
            {
                {"value", new InArgument<int>(42)}
            }
        }
    };
 
    var receive = new ReceiveReply()
    {
        Request = send,
        Content = new ReceiveParametersContent
        {
            Parameters =
            {
                {"GetDataResult", new OutArgument<string>(getDataResult)}
            }
        }
    };
 
    var workflow = new Sequence()
     {
         Variables = { getDataResult },
         Activities = 
        {  
            new CorrelationScope() 
            {
                Body = new Sequence() 
                { 
                    Activities = { send, receive } 
                } 
            }, 
            new WriteLine() { Text = getDataResult } }
     };
 
    return workflow;
}

 

Update: See here for the code when using a RequestReplyCorrelationInitializer.

 

Enjoy!

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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

Attached are the PowerPoint sheets and samples from my online Window Workflow Foundation 4 presentation today.

 

Let me know if you have any questions.

www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu

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