Monthly Archives: September 2005

Dave said it could be done, so I had to do it ;-)

If you are interested in WWF and workflow – you should definitely (if you haven’t already) read Dave Green’s blog.  Dave is an architect on the WWF team, and quite the smart guy.

In this post – he said WWF was flexible enough to allow for a random workflow execution. Well – I guess I kind of took that as a challenge.  Fun stuff.  This (RandomWorkflow.zip (49.22 KB)) project has a project in it where the workflow will execute all child activities in a random order.  To what purpose?  None really – just having fun trying to implement an IRootActivity and the random executor sounded like fun.

One quick tip, if you are building an IRootActivity (this is what is allowed as the top level of a Workflow – it really is an Activity that implements that special interface), build it in a non-workflow project (just a regular C# or VB.NET class library project).  In a workflow project – the workflow project system won’t let it compile because it detects there isn’t an ID set on it.

WWF is sooooooo elegant

In my last post I talked about the ThreadingService.  This is the Service that WWF uses to schedule workflow execution. The DefaultThreadingService uses the CLR ThreadPool to queue work items onto the CLR ThreadPool’s threads.  The effect of this as I said in that post is that an executing Workflow doesn’t keep a process alive.

What is so elegant is WWF’s extensibility model.  At every level of WWF things are extensible, so I can implement my own ThreadingService, that can use threads that have their IsBackground property set to false, which will allow that thread to keep a process running.   It was so simple  (especially since I used Mike Woodring’s ThreadPool implementation- thanks Mike!).

The ThreadingService looks like this:

namespace KeepAliveThreadingService
{
    public class KAThreadingService : ThreadingService
    {
        public override void Schedule(System.Threading.WaitCallback callback, Guid instanceId)
        {
            _pool.PostRequest(callback, new object[] { instanceId });
        }
        public override void Start()
        {
            _pool = new DevelopMentor.ThreadPool(2, 25, “KeepAlive”);
            //have the thread pool not use background threads
            //so workflow threads will keep the process alive
            _pool.IsBackground = false;
            _pool.Start();
            
        }
        DevelopMentor.ThreadPool _pool;
        public override void Stop()
        {
            if(_pool.IsStarted)
                _pool.Stop();
        
        }
}

 

An example of the usage would be here:

namespace WorkflowConsoleUseKeepAliveThreadPool
{
    class Program
    {

        static void Main(string[] args)
        {
            WorkflowRuntime wr = new WorkflowRuntime();

            KeepAliveThreadingService.KAThreadingService service = new KeepAliveThreadingService.KAThreadingService();
            wr.AddService(service);
            wr.StartRuntime();
            wr.WorkflowCompleted += delegate
            {
                service.Stop();//stop the thread pool – will allow the process to exit

            };
            Type type = typeof(WorkflowConsoleUseKeepAliveThreadPool.Workflow1);
            wr.StartWorkflow(type);
            AppDomain.CurrentDomain.ProcessExit += delegate
            {
                Console.WriteLine(“Stopping Runtime”);
                wr.StopRuntime();
            };
        }

    }
}

 

Now – with this design there still are threading issues – like making sure that all running workflows are done before stopping the threadpool, if you don’t stop the threadpool – then the process doesn’t die (which is why if you were going to use something like this you’d want to make sure all your race conditions were taken care of).  But it is a good example of how great the WWF runtime is IMO.

Other services that are used by the WWF runtime (which of course are also pluggable):

StatePersistenceService (there is a SqlStatePersistenceService implemenation included in the library).

TimerService

TrackingService.

WorkflowTransactionService.

Download the whole solution – KeepAliveThreadingService.zip (93.93 KB)

Interesting (or perhaps not) WWF threading tibit

When you call WorkflowRuntime.StartWorkflow – the workflow itself is started from one of the CLR thread pool threads (this is done via the Runtime’s ThreadingService – this is what the default threading service does).

What this means is that a running workflow will *not* keep a process alive – since the the CLR thread pool threads have IsBackground set to true.  Even the DefaultThreadingService’s thread (it uses one thread to cycle over any workitems it has in its internal queue – any workflows that need executed) won’t keep the process alive.

On a threading related note – please don’t assume (just like in BizTalk) that the parrallel activity spawns multiple threads – it doesn’t.  There is a scheduler inside of the workflowruntime that will schedule execution of activities instead of each parrallel branch in parrallel – but not on multiple threads (each contained activity is queued).  So the execution of each branch happens in parrallel  – but not on multiple threads – each branch has its first activity executed first before the scheduler executes the second activity and so on.

More on the dynamic nature of WWF

So – I have to admit – this is pretty much a blatant ripoff of something I saw Dharma do during Don’s first talk that involved workflow on Wed at the PDC, but since I am not sure if he’ll post the code or not – I wanted to put it out there. (just to be clear – this is my attempt to copy what I saw Dharma do – I don’t have access to his code).  It fits in pretty well with my chapter on Dynamic Workflow from the book.

UPDATE: Dharma posted his code on his blog – here.

It just shows how really simple it is to create applications that not only host workflow – but that also can dynamically compile workflows (which really brings the potential usage model for workflow up a couple of notches).

Here is my simple XAML/XOML compiler like the one Dharma used.  The first argument is the XOML file itself, and any additional assemblies you use (like ones that contain Activities that the workflow uses) are passed in as additional arguments.  The AssemblyResolve event handler is necessary because the workflow runtime tries to use the Load context when loading assemblies (which makes total sense).

using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Threading;
namespace XAMLCompiler
{
    class Program
    {
        static void Main(string[] args)
        {

            
            WorkflowCompiler wc = new WorkflowCompiler();
            WorkflowCompilerParameters prms = new WorkflowCompilerParameters();
            if (args.Length > 1)
            {
                for (int i = 1; i < args.Length; i++)
                    prms.ReferencedAssemblies.Add(args[i]);

            }
            WorkflowCompilerResults wcr = wc.CompileFromFile(prms, args[0]);
            if (wcr.Errors.Count > 0)
            {
                foreach (CompilerError ce in wcr.Errors)
                    Console.WriteLine(ce.ErrorText);
            }
            Assembly a = wcr.CompiledAssembly;
            if (a != null)
            {
                AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs rargs)
                {
                    if (rargs.Name == a.FullName)
                        return a;
                    else
                        return null;
                };
                //extract the workflow
                Type t = a.GetTypes()[0];
                using (WorkflowRuntime wr = new WorkflowRuntime())
                {
                    wr.StartRuntime();
                    wr.WorkflowCompleted += delegate { wait.Set(); };
                    wr.StartWorkflow(t);
                    wait.WaitOne();
                    wr.StopRuntime();
                }

            }

        }
        static AutoResetEvent wait = new AutoResetEvent(false);

    }
}

 

So if this code is compiled into a console application I can pass the following xaml (ok xoml right now – but I understand they are changing this to xaml to match WPF) as the first argument:

<?Mapping XmlNamespace=”ComponentModel” ClrNamespace=”System.Workflow.ComponentModel” Assembly=”System.Workflow.ComponentModel” ?>
<?Mapping XmlNamespace=”Compiler” ClrNamespace=”System.Workflow.ComponentModel.Compiler” Assembly=”System.Workflow.ComponentModel” ?>
<?Mapping XmlNamespace=”Activities” ClrNamespace=”System.Workflow.Activities” Assembly=”System.Workflow.Activities” ?>
<?Mapping XmlNamespace=”RuleConditions” ClrNamespace=”System.Workflow.Activities.Rules” Assembly=”System.Workflow.Activities.Rules” ?>
<?Mapping XmlNamespace=”MyActivities” ClrNamespace=”MyActivities” Assembly=”myactivity, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null” ?>
<SequentialWorkflow x:Class=”MyWorkflow”  ID=”MyWorkflow” xmlns:x=”Definition” xmlns=”Activities”>
    <ns0:MyActivity ID=”myActivity1″ xmlns:ns0=”MyActivities” />
</SequentialWorkflow>

The second argument would be the name of the assembly that contains the MyActivities.MyActivity type – which you can see here

using System;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.ComponentModel;

namespace MyActivities
{
[ToolboxItem(typeof(ActivityToolboxItem))]
public class MyActivity : Activity
{
protected override Status Execute(ActivityExecutionContext ctx)
{
Console.WriteLine(“HEllo from My Activity”);
return Status.Closed;
}
}
}

This assembly can be compiled on the command line like this:

csc /t:library /r:”C:WINDOWSMicrosoft.NETFrameworkv2.0.50215Windows Workflow FoundationSystem.Workflow.ComponentModel.dll”  myactivity.cs

 

You can download the full source here – XAMLCompiler.zip (22.4 KB)

Code download and state machine example

If you are reading the Presenting Windows Workflow book or if you just want to see a nice collection of code samples – you can download the code done in the book at the Sam’s web site – here.

For Chapter 12 – I wrote a worfklow that has some dynamic update capabilities (this is one of the features that got the most ohhs and aahhs from the crowd at the PDC in Don’s and Dharma’s session).   Dynamic update means that you can add and remove activities from a workflow, either on a per-instance basis (so you can add an activity to a running workflow instance) or you can modify a workflow and persist the new workflow (easier done if the workflow has been persisted to xaml, but possible even if the workflow tree is compiled).  There are of course restrictions on this, the main one being that the workflow has to be in a valid state after the modification.   You cannot make a change to a workflow at runtime that you couldn’t have done at design-time (essentially the same infrastructure is making both the design-time and the runtime checks).

For the book I wrote a sample based on a workflow a patient might go through when admitted into a hospital.  The idea was to create a workflow that might need to be modified, essentially a workflow with an open point in it, where different activities could get dynamically loaded based on need.  So as a patient was treated, the main application (the workflow host) could pump messages into the workflow telling the workflow itself how to dynamically update by create an instance of the activity in question (a “treatment” activity) and then executing it.

The way I modeled the workflow for the book was as a sequential workflow.  I made that decision just to keep the example as simple as possible.  If I was implementing that application for real, I would do it as a state-machine workflow.

State-machine workflows really are just a better way to model certain kinds of workflows.  Instead of having the flow of control be “top to bottom”, a state-machine workflow is modeled as a set of discrete “states”.  At each state you can have the ability to intialize, and then listen for events.  When the events come into the workflow you can then execute some activities in a sequential manner, and then potentially change the state of the workflow to another state.  This kind of workflow is good for things like page flow in asp.net, or running a wizard in windows forms or Avalon.

So I ported my sample to use a state-machine workflow instead of a sequential workflow. 

Here is where you can download the sample – PatientWorkFlowHostSM.zip (139.69 KB).

The sample follows the book fairly closely – the idea being that through the UI (the host application) the end-user picks a treatment activity for the workflow to execute, and the workflow enters the “treat” state and dynamically loads that activity and executes it.  This can happen over and over again until the patient is discharged.  There is a base “treatment” activity from which all the other treatment activities derive from.

What I will post soon is an extension this sample to include a designer – which would allow a high-end hosptial staff member to actually compose new “treatment” activity from built in activities – add these to the application and be able to dynamically load those new treatments.  This kind of touches the surface of the kind of sophisticated applications that are possible with WWF.

How am I involved with Windows Workflow Foundation

So I said I felt like a huge burden had been lifted when they announced Windows Workflow Foundation (WWF) during Wednesday’s keynote. Here’s how I’ve been involved in WWF.

I’m a co-author on the Presenting Windows Workflow Foundation book from Sam’s:

You can see more information on the book here.

Or view on Amazon.com (which doesn’t seem to be working quite yet).

Edit – Amazon doesn’t have it up yet – but Barnes and Noble does – and we are ranked 109 at the moment!

Also I’d like to announce here (its already been announced at the PDC and is linked from the MSDN Workflow Center) that I’m going to be authoring a 5-day custom course for DevelopMentor on WWF – Essential WWF – you can click here for more information.

As I said – with the PDC going on I haven’t had time to post any real technical content about WWF – but I promise it is coming RSN.

Whew – finally I can POST!

I am sitting in on Eric Rudder’s keynote at the PDC and he just publicly announced Windows Workflow Foundation (WWF).  I’ve been involved in WWF for quite a while now, and I am really excited about what this technology can do for developers.  Now I can finally talk about it here!  Yipee!  More technical content about WWF to come.

Just like a lemming – see you at the PDC

Since everyone else seems to be doing it – I’ll jump right in and post :).  I will be at the PDC.  Come by the DevelopMentor booth – we’ll be having some cool stuff to show – but we can’t show it until after the keynote on Wed ;-).   After that keynote I will be blogging about stuff I’ve been working on for the past few months but haven’t been able to talk about.

If any of the people who read this out in blog land see me at the PDC please stop by to say hi!  I’ll try to see if I can score our t-shirt for you (again – not till after Wednesday’s keynote – so don’t even try before then ;-)).  Also, since I am local – if anyone is looking for something fun to do or looking for a good place to eat – hit me up for suggestions.