Tag Archives: wf

WF 4.0: Hello World Workflow Services

Building a workflow service using 4.0 gives a very neat set of capabilities from both WF and WCF. Similar to WCF we can fully define a workflow either in code or otherwise just using Xaml. Here I chose a fully code based approach. If you just want the source you can download it here. (2.20 kb)

The Service Contract

We can take a either a workflow first or a contract first approach. To keep things simple, I have a very bare service contract as shown below and proceed towards the workflow definition from that.

[ServiceContract]interface IService1{    [OperationContract]    void GetData();}
I also hold on to the contract description. This is just for convenience and to avoid some constants like service name etc.
ContractDescription description = ContractDescription.GetContract(typeof(IService1));

The Operation

The idea here is to build the service bottom up from operation. I can think of my workflow as composition of activities. The functionality of my service is quite simple here. All I do is write a message to the console. So the body of my operation will look something like this.

new WriteLine{    TextWriter = Console.Out,    Text ="Hello Workflow."},

The Implementation

Now I need to make this an operation. An operation has an associated message exchange pattern, commonly referred to as MEP. In my case it’s a simple receive reply pattern. This means I have to compose my work in between a Receive and a SendReply. I am also going to tie this all up in Sequence as shown below.

Sequence sequence = new Sequence{    Activities =    {        receive,        new WriteLine        {            TextWriter = Console.Out,            Text ="Hello Workflow."        },        reply    }};

The Message Exchange

The next thing is to lay out the Receive and Reply which will define the wire format of the messages.

The receive needs to know the operation that the client can invoke. Also I need to make sure that the workflow can be instantiated by this particular operation. For this I need to set the CanCreateInstance property. Also the serializer used by default is the DataContractSerializer.

Receive receive = new Receive{    OperationName = description.Operations[0].Name,    CanCreateInstance = true};

The Reply is send back to the client using my SendReply activity. This reply is a part of a conversation and it needs to know which request it would respond to. I can do this by pointing the SendReply to the instance of the corresponding Receive. Also I don’t have any data here so it is an empty parameter list. (the operation is not Messages contract based in my case but this is generally a better option for more control and performance.)

SendReply reply = new SendReply{    Request = receive,    Content = new SendParametersContent { }};

I now need to define the workflow service which would hold this Sequence and also need to specify the full name of the service definition. For this I can use the description object to get the name, namespace etc.

WorkflowService serviceDefinition = new WorkflowService{    Body = sequence,    Name = XName.Get(description.Name, description.Namespace)};

The Host

The next step is to give this definition to the workflow service host so that it can start the service. Here I self host the workflow as shown below.

WorkflowServiceHost host = new WorkflowServiceHost(serviceDefinition,                                        new Uri("http://localhost:8080"));host.AddServiceEndpoint(serviceDefinition.Name, new BasicHttpBinding(), "");host.Open();

The Test

Finally I can test the workflow using a simple proxy.

ChannelFactory cf = new ChannelFactory(new BasicHttpBinding(), "http://localhost:8080");IService1 proxy = cf.CreateChannel();proxy.GetData();((IChannel)proxy).Close();

Channel9: Monitoring WF Services

  10-4 Episode 24: Monitoring Workflow Services

By Ron Jacobs -

One of the great advantages to building services with WCF and Windows Workflow 4 is that the environment is instrumented with loads of events that allow you to track what exactly is happening.  This is useful for health monitoring, troubleshooting and other scenarios like auditing and compliance.  In this episode I’ll take you through the monitoring lab from the Visual Studio 2010 training kit and show you how you can take advantage of these powerful capabilities.

Media_httpgomicrosoft_divff

Synchronous execution in WF

My previous post points to those of you who want a first look at WF 4.0.

This post focuses more on how to get one of the best Async Runtimes to get executed in sync(shooting myself in the foot here). This was inspired by a question from a friend of mine who wanted to play with the gory stuff like TLS and other unmentionables. For people already working in WF 3.X you would be familiar with the Manual Workflow Sheduler Service. If not then to sum it up its a way to run your activity synchronosly.

That being said I am not sure on how familiar you are with the little known but very cool SynchronizationContext that got introduced with 2.0. WF leverages this to solve this fundamental problem. I would like to first explain using the default sync context in a winform app to showcase how to avoid invoke calls when updating your control text using a workflow.

 

using System.Windows.Forms;

using System.Threading;

using System.WorkflowModel;

namespace WinformTest

{

    class Program

    {

        static void Main(string[] args)

        {

            Application.Run(new MyForm());

        }

        class MyForm : Form

        {

            SynchronizationContext ctx;

            Button button;

            public MyForm()

            {

                ctx = SynchronizationContext.Current;

                button = new Button();

                button.Click += new System.EventHandler(b_Click);

                this.Controls.Add(button);

            }

            void b_Click(object sender, System.EventArgs e)

            {

                ChangeText we = new ChangeText();

                we.form = this;

                WorkflowInstance instance = WorkflowInstance.Create(we, this.ctx);

                instance.Resume();

            }

            class ChangeText : WorkflowElement

            {

                public MyForm form;

                protected override void Execute(ActivityExecutionContext context)

                {

                   form.button.Text = “Changed”;

                }

            }

        }

    }

}

 

Now where did that control.Invoke call disapper. Thats where SynchronizationContext kicks in. To show a simple sync context for synchronous execution you could just pass in your own Sync context as below. Here the idea is to set a value on the TLS and execute a workflow with and without the context and showcase what value comes of the TLS in each case. Read this for understanding how to implement your sync context.

   

using System;

using System.Threading;

using System.WorkflowModel;

using System.Collections.Generic;

namespace SyncContextSample

{

    class Program

    {

        static void Main(string[] args)

        {

            ExecuteWorkflow();

        }

        private static void ExecuteWorkflow()

        {

            DataItem.id = Thread.CurrentThread.ManagedThreadId.ToString();

            DataItem.DisplayData();

            BlockingNotifySynchronizationContext ctx = new BlockingNotifySynchronizationContext();

            SynchronizationContext.SetSynchronizationContext(ctx);

            Thread t = new Thread((env) => ExecuteWF(env));

            t.Start(SynchronizationContext.Current);

            ctx.EnsurePump();

        }

        private static void ExecuteWF(object context)

        {

            WorkflowInstance instance = WorkflowInstance.Create(CreateRootActivity(), (SynchronizationContext)context);

            instance.Resume();

        }

        public static WorkflowElement CreateRootActivity()

        {

            return new DipslayTLS();

        }

    }

    public class DipslayTLS : WorkflowElement

    {

        protected override void Execute(ActivityExecutionContext context)

        {

            Console.WriteLine(“Executing Activity”);

            DataItem.DisplayData();

        }

    }

    public class DataItem

    {

        [ThreadStatic]

        public static string id;

        public static void DisplayData()

        {

            Console.WriteLine(“Thread: “ + System.Threading.Thread.CurrentThread.ManagedThreadId + ” has data: “ + id);

        }

    }

    class BlockingNotifySynchronizationContext : SynchronizationContext

    {

        Queue<KeyValuePair<SendOrPostCallback, object>> q = new Queue<KeyValuePair<SendOrPostCallback, object>>();

        ManualResetEvent mre = new ManualResetEvent(false);

        public BlockingNotifySynchronizationContext()

        {

            SetWaitNotificationRequired();

        }

        public override int Wait(

                IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)

        {

            //Console.WriteLine(“Begin wait: {0} handles for {1} ms”,

            //    waitHandles.Length, millisecondsTimeout);

            while (q.Count != 0)

            {

                KeyValuePair<SendOrPostCallback, object> kv = q.Dequeue();

                kv.Key(kv.Value);

            }

            int ret = base.Wait(waitHandles, waitAll, millisecondsTimeout);

            //Console.WriteLine(“Finished wait {0}”, Thread.CurrentThread.ManagedThreadId);

            return ret;

        }

        public override void Post(SendOrPostCallback d, object state)

        {

            q.Enqueue(new KeyValuePair<SendOrPostCallback, object>(d, state));

        }

        public override void Send(SendOrPostCallback d, object state)

        {

            throw new NotImplementedException();

        }

        public void EnsurePump()

        {

            while (true)

            {

                mre.WaitOne(0, false);

            }

        }

    }

}

   

You would get something like this

Thread: 1 has data: 1
Thread: 1 has data: 1
Executing Activity
Thread: 1 has data: 1
Executing Activity
Thread: 3 has data:

Just as note this is more of rare case and should be used only when you know you need it cause you loose all the benefits of going totally async and taking advantage of the WF 4.0 runtime robustness.

Musings on Workflow Terminated and Exception Handling

When a workflow runs an activity and that activity throws some kind of unhandled exception you would notice that your workflow get terminated. I have see in many articles that explain how the termination happens. Now this is fine only for a rare set of hello world scenarios. When it comes to an enterprise application we usually expect to notify that some error has happened and probably redo our work.

 

Now if the workflow terminates how the heck are we supposed to re-execute the workflow cause its already terminated right :) and you get the “Workflow not found in persistence store” when you use  a store like SQL . Now this happens when our activity doesn’t have a fault handler associated with this it. So one of the solutions we tried for state machine error handling was this.

  1. View the FaultHandlers in the event driven activity in the state.
  2. Add a new Fault activity and set the fault type to System.Exception (this is to capture generic faults and nothing fancier) you can customize this but then again we are trying to avoid workflow termination here.
  3. in the fault handler use a CallExternalMethodActivity.
  4. How to catch the exception – You can promote the Fault property as a dependency property. Another easy way would be to call a methodInvoked(ie the method to be executed before the callExternal method is invoked) and in that access the exception as the fault property in the parent of the callExternalMethodActivity.
  5. You can then pass on what ever data you get to the external method.

I believe there are many more ways of fault handling. I even know of cases where customers hacked the stored procedure of Insertworkflow to avoid workflow deletion on termination.

Anyway this is just on of the easier ways out to clean up incase you have better practices do leave a link :)