Monthly Archives: June 2006

A workflow sample

I built a sample (you can download it here) based upon a question asked in the WF forums last night (you can see the post here).

I wanted to post it here so I could point out some of the interesting features it has in it.

First – there is a CallWorkflowActivity – which creates and executes another workflow in a synchronous manner.  The WF built-in InvokeWorkflowActivity invokes another workflow – but does it asynchronously.    It turns out to do is synchronously you have to build a custom activity – mostly because synchronously you have to make some assumptions about the workflow you are calling.   In my example – the assumption I am making is that the “called” workflow will have parameters to pass in, and parameters that are return – but there is no other communication between the host and the workflow.

Now – since my CallWorkflowActivity can’t talk to the WorkflowRuntime directly – in order to make it work I implemented a service to add to the WorkflowRuntime.  Why can’t the activity talk to the WorkflowRuntime?  Well that could create some interesting problems – like what if an Activity could ask the WorkflowRuntime to persist the WorkflowInstance the Activity is being executed inside of?  The Activity would be executing waiting for persistence, the WorkflowRuntime would be waiting to persist until the Activity completed – classic deadlock.

 

InvokeWorkflow uses a service that is installed by default – IStartWorkflow – which works create in the async case – but doesn’t work in the sync case.  So I just created a custom service.  One aspect of WF that I try to emphasize to people when I do my WF trainings is that even though there are well-known services inside of the WorkflowRuntime – you can add any number of services which won’t be known to the WorkflowRuntime – but can be used by your Activities.  In fact this is necessary in some cases (like this one) where the code you want to execute would be “illegal” inside of Activity.Execute (things like spawning threads or anything that would go against the model of WF in terms of what Activities should do).  Now in this case I made the Service derive from WorkflowRuntimeService because my service needs the WorkflowRuntime to do its job, and that is the easiest way to get a reference to the WorkflowRuntime.   But it isn’t a requirement that a service added via WorkflowRuntime.AddService derive from WorkflowRuntimeService. 

So here is how my service executes a workflow:

public class CallWorkflowService : WorkflowRuntimeService
{

public void StartWorkflow(Type workflowType,Dictionary<string,object> inparms,Guid caller,IComparable qn)
{
WorkflowRuntime wr = this.Runtime;
WorkflowInstance wi = wr.CreateWorkflow(workflowType,inparms);
wi.Start();
ManualWorkflowSchedulerService ss = wr.GetService<ManualWorkflowSchedulerService>();
if (ss != null)
ss.RunWorkflow(wi.InstanceId);
EventHandler<WorkflowCompletedEventArgs> d = null;
d = delegate(object o, WorkflowCompletedEventArgs e)
{
if (e.WorkflowInstance.InstanceId ==wi.InstanceId)
{
wr.WorkflowCompleted -= d;
WorkflowInstance c = wr.GetWorkflow(caller);
c.EnqueueItem(qn, e.OutputParameters, null, null);
}
};
EventHandler<WorkflowTerminatedEventArgs> te = null;
te = delegate(object o, WorkflowTerminatedEventArgs e)
{
if (e.WorkflowInstance.InstanceId == wi.InstanceId)
{
wr.WorkflowTerminated -= te;
WorkflowInstance c = wr.GetWorkflow(caller);
c.EnqueueItem(qn, new Exception(“Called Workflow Terminated”, e.Exception), null, null);
}
};
wr.WorkflowCompleted += d;
wr.WorkflowTerminated += te;

}

}

 

Notice the use of anonymous delegates really helps in this case.  No need to keep state about workflow instances around in the service – the anonymous delegates get registered and unregistered for each Workflow execution. 

To get the data back to the Activity – the WorkflowQueue name that the Activity created – so the Activity can return ActivityExecutionStatus.Executing from Execute – and it waits for an item to come back on the Queue.

If the Workflow terminates – the service sends an exception into the WorkflowQueue – which causes that exception to promolgate to the workflow itself.

Another interesting part of this sample is the use of the WorkflowParameterBindingCollection – similar to the way that the InvokeWorkflow,CallExternalMethod and other built-in activities do.  Make note of the DependencyProperty declaration and the call to base.SetReadOnlyProperty in the constructor – both are necessary to get Collections to serialize correctly into your .designer file.

Had a great TechEd

For those of you that came to my session on building custom activities with Windows Workflow Foundation (WF) – here is the code –

teched2006.zip (385.72 KB) (of course if you didn’t come feel free to download the code – perhaps I will make a post about it soon).

I ran into tons of people I knew, got to meet lots of cool new people – and overall just had a great time.  One thing that struct me as cool was the gift they gave exernal speakers – on Monday night when I got back to my room there was a bottle of wine in a teched holder with a teched branded stopper – here is a picture (sans wine which was – umm – already consumed and didn’t make it home):

 

Which I found an interesting contrast to what we got last year – the only piece of which I have left you can see in this picture (I added the “me” to the middle person – just so I could feel closer to Bill and Steve):

 

I think I like this year’s gift a little more :).

I was also reminded of the little squishy guys by the squishy blocks they gave away in the WF booth – which made CNET – http://news.com.com/2300-1012_3-6083455-2.html?tag=ne.gall.pg

 

I’ve got more to post about TechEd 2006 US – but it’ll have to wait as I am home now and going out for sushi with Shannon :)

WF Serialization Part One and a half

So as an update to my earlier post about WF and Serialization I’ve discovered another interesting little serialization issue that relates to activity execution contexts.

If you aren’t familiar with AECs – here are two links to get you going:

http://msdn.microsoft.com/msdnmag/issues/06/01/WindowsWorkflowFoundation/

http://blogs.msdn.com/advancedworkflow/archive/2006/03/21/557121.aspx

The effect this WF feature has on serialization relates to how they’ve implemented these spawned execution contexts.  Let’s take a simple custom activity inside of a WhileActivity:

 

As the While executes – each execution cycle creates a new spawned AEC for activity11.

Let’s assume that the Activity has a private field of a type that isn’t serializable (I’ll re-use the Point type from my last serialization post):

public class Point
{
public int x;
public int y;
}

So what happens?  Well until I use my custom activity inside of  another activity that creates a spawned AEC (or like the last post – until I add a WorkflowPersistenceService) – everything is fine.  But once you put this Activity inside of a WhileActivity – BAM – Serialization exception.

Why?  To understand try this test – create a custom activity – set a breakpoint inside of the constructor (or use Debug or Console.WriteLine to write out a message from the constructor) – notice that your constructor gets called twice (see my earlier post about WorkflowInstance.GetWorkflowDefintion about the second call) and only twice.  Even if you have a While that executes a hundred times – your constructor is only called twice.  This is because on each subsequent spawned ActivityExecutionContext – the runtime calls Activity.Clone to create a new instance – not “new”.  

Of course this is where the serialization exception happens – because the implemenation of Activity.Clone uses Activity.Save – which is the same method used by the WorkflowPersistence infrastructure.   This is the method that uses a BinaryFormatter to serialize the Activity type (they clone the “extra” instance they keep around as the “definition”).  So they create a new Activity instance for each AEC by serializing the existing instance and deserializing into a new instance.  Which is why your constructor doesn’t get called on each execution of the While loop.

Fixing this problem is the same as fixing the serialization issue with WorkflowPersistence.  It just goes to show you though – that you can have serialization “issues” (when I say issue I don’t mean to imply that WF has anything wrong with it – quite the opposite I find the implemenation of AEC’s and Clone to be really well done) even if you don’t use WorkflowPersistence.

Pimping my TechEd Session

If you are coming to TechEd next week – come to my session and learn about building custom activities in workflow:

CON333  (WinFX) Windows Workflow Foundation: Creating Custom Activities for Workflows
Day/Time: Thursday, June 15 1:00 PM – 2:15 PM  Room: 254 AB
Speaker(s): Jon Flanders
Activities are the building blocks of workflows much like controls are the building blocks of ASP.NET pages or Windows Forms. In Windows Workflow Foundation, activities are used to encapsulate execution logic, communicate with the host and factor a workflow into reusable components. In this session, learn how to create custom activities and re-usable libraries of workflow building blocks.
Track(s): Connected Systems
Session Type(s): Breakout Session
Session Level(s): 300
 
Also – I’ll be hanging by the Windows Workflow TLC area when I’m not speaking or attending sessions – come on by and say “Hi” and ask about WF.   (for you literal people out there – feel free to say hi if you see me outside of the TLC area as well – ;-))

Yes – I still love Atlas

I have to give kudos to the ASP.NET Team.  I really love the Atlas programming model.  I’d like to tell you that my Atlas based Workflow Designer is ready – but it isn’t.  But – I did re-build the WF WorkflowMonitor sample application tonight as an ASP.NET Atlas based application (oh – and I also did is as a non-Atlas based ASP.NET application for those of you that are still suck doing flicker-based PostBacks ;-)).

What is Workflow Monitor?  Well the Windows Workflow Foundation runtime will take advantage of a TrackingService  – if one is found inside of the WF runtime.  The runtime ships with a SqlTrackingService – which by default tracks all Activity events.  See my post on the WF forums if you have never gotten tracking up and running for the steps I think people should go through to get the zen of WF tracking – http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=438583&SiteID=1.

One of the coolest steps is to use the Workflow Monitor sample that ships with the WF SDK.  It is a Windows Forms application that will (assuming you are using the SqlTrackingService and have added your workflow assembly as a reference or into the directory where Workflow Monitor is running) show you what has executed in terms of your Workflows.  If you set the SqlTrackingService.IsTransactional property to false, you can even see this in semi-realtime as the SqlTrackingService will write each event to the database as it happens rather than batch them (all writes are done transactionally regardless of the IsTransactional property BTW).

Why is this cool?  Well – to me it shows off one of the big features of using WF – visibility.  You can even show this to your pointy-haired bosses and they might start to see the advantage of WF.  Here is a screen shot of Workflow Monitor in action:

 

So what’s so cool here?  Well two of WF’s major features are being emphasized.  First – Visibility (sometimes referred to as Transparency – I like the simplier term).  At runtime and after runtime we can actually *see* what our processes have done.  The second major WF feature displayed is the designer re-hosting inside of another application other than Visual Studio. Although the designer really plays into visiblity – the fact that we can host the designer in any application royalty free I think is a huge bonus feature of WF.  Oh – not to mention tracking and the capabilities of the out-of-box SqlTrackingService.

So given my recent perseveration with Atlas and the WF designer I thought – wow wouldn’t that be a cool combination? Having the visibility into your WF processes for the masses – even those who don’t have the WF runtime on their machines – or those who you don’t want to have to deploy the Workflow Monitor sample (which could be made into a pretty deployable click-once application very easily).

So here we go – unlike last nights tease post – not only will I show you a Workflow Monitor application hosted in ASP.NET – I’ll post the code as well.

So here is my version of the Workflow Monitor in ASP.NET (which at the designer level borrows alot of code from the Workflow Monitor sample just to keep it consistent with what people have become used to).  First let’s look at the Atlas version.  Why an Atlas version?  Well first of all as I mentioned I think Atlas rocks.  But it rocks for a very good reason (and that reason isn’t that pointy-haired bosses know the term AJAX now and think they are so cool because of that).

Atlas and Ajax is cool because is gives the user the best browser experience possible.  Clearly the smart-client experience is going to be superior.  But if you have to go with a browser only application – why not make that application as easy as possible to use – and as nice as possible to look at.  I think AJAX browser applications do do that better than non-AJAX ones, and I think that Atlas is a great framework if you are building ASP.NET applications.

So in the spirit of cross-browser love – I am going to show you all the screen shots in Firefox.  :)

Here is my atlas/ajax based Workflow Monitor application in Firefox:

 

This is the ASP.NET Workflow Monitor in Firefox, before any workflow has been selected from the list of tracked workflows in the GridView on the right side of the page (to make this managable for more than a demo you’d want to restrict the time-frame of the query for these instances or give the use the ability to restirct the query – you can find how to do that from the Workflow Monitor sample itself).  Once I select an workflow from the list of tracked workflows on the left – I get the following (because it is using Atlas and an UpdatePanel there is no post-back flicker – only the workflow image itself is being updated:

 

Again – I have to tip my hat to the ASP.NET team on the job they did on Atlas, I never would have believed I could just write my ASP.NET page using the declarative ASP.NET model, add a few extra declarations and have a cross-browser AJAX application. But I have to say wow – they did it.  My contribution to this demo pretty much pales in comparison to theirs.

So how does this code work?  Well quite a while ago I posted about hosting the WF desinger in ASP.NET using a control and a handler to render a workflow as an pure image.  The worklow designer (WorkflowView actually) makes this option availlable as a public method.  The post I made last night about a WF Atlas based designer is also based on that code – as is this code (for honest disclosrure I borrowed much of the Workflow Monitor code directly for the worklfow drawing and use of the tracking API).

All that really happens is that each time you select a workflow from the list in the right-side GridView, a post-back is done to the server.  On the server-side some code in the page executes and retrieves the tracked workflow from the SqlTrackingService Query API (the same code that the Workflow Monitor example uses) based on its InstanceId. That information (the SqlTrackingServiceInstance)  is stored in ASP.NET session.  The HttpHandler – once it gets a request from the current session uses the SqlTrackingService instance to generate the image for the current workflow (the code from Workflow Monitor already decorated the current worklfow with special glyphs or images based on the execution status).

Just like the in Windows Forms version, this version loads the workflow into the Workflow Designer, but unlike that version which shows the results to the screen, this version captures an image (you can do this from the Workflow menu in Visual Stuiod 2005 as well when you are designing a workflow).

In the Atlas version there is no flicker and the browser app appears fairly close to Workflow Monitor.  With a Atlas timer – you could replicate the Workflow Monitor fucntionality almost completely and always hightlght and select the most recent workflow.

Again – if you are unlucky enough to be stuck doing ASP.NET without Atlas – I built a version that removes all the Atlas references and tags, and does work – just looks pretty bad since there is lots of post-backs and white screens in the browser.   You can download – either version (or hey download both versions and see the beauty of atlas – and remember you have to download and install the latest version of atlas for this to work – as well as beta 2.2 of WF) :

AtlasWorkflowMonitor.zip (697.68 KB)

NonAtlasWorkflowMonitor.zip (292.61 KB)

If you are stuck not using Atlas – this might a good time to introduce your pointy-haird boss to AJAX.  I am sure you’ll be using Atlas in a very short amount of time ;-)

How I spend my Sunday afternoon – playing with Atlas

So before I spent all my time playing with BizTalk and Windows Workflow Foundation (WF) – I used to do quite a bit of ASP.NET (there are even a few old ASP.NET posts laying around on my blog :)).

Before that of course I used to do ASP/COM.  I joke that I started learning about and teaching AJAX back in 1999 (thanks to the brilliance of John Lam ).

I looked at Atlas a little last fall, but never got into it very deeply.  I decided to play with it over the weekend, and I have to say – I am really really impressed.  I decide to tackle what I thought would be a pretty interesting application – hosting the WF designer in an HTML only – AJAX based application.

I had already done the code to get images to be generated from the Workflow Designer on the server instead of hosting the designer in a browser as a control (see http://www.masteringbiztalk.com/blogs/jon/PermaLink,guid,28601754-c305-4597-a0a6-48f32be1eddb.aspx).

So I built a little teaser avi of the functionality.  Sorry no code yet – it needs some work before it is ready for public consumption – but even at this point I have to say I think it looks pretty cool .  I think building a real design app around it will be possible. 

So  you can download the avi of my Windows Workflow Foundation – AJAX based designer application built with Atlas  here – go ahead its only 411k ;-).  I’d post images – but I’ve posted too many of them lately.  Hopefully I’ll have time to get it done before TechEd next week. 

Anyone who is coming to TechEd – stop by the TLC Workflow booth – that is where I will be spending most of my time when not going to sessions or doing my session.  My session will be on Building Custom Activities with WF.  You can see the time here – http://www.msteched.com/content/sessions.aspx (you can find it by searching for Workflow – there are going to be a number of great talks you should attend if you are interested in learning more about WF).

WorkflowDesigner hosting and Rules

So Matt posted a link to a new updated example from the Workflow team – you can get it here

This sample unfortunately has the same flaw that many of the samples using the Workflow Designer in a re-hosted (e.g. not VS.NET) situation have – is that the Rules editing dialogs are busted.

Here is what happens if you use the above sample.  It creates a new SequentialWorkflowActivity.  Go to the DynamicUpdateCondition property.

Select to have this condition be a “Declarative Rule Condition” (which will be a dynamically invoked RuleCondition – which is what is generally what you want in most Activities/Workflows).

Here is what happens in this designer when you click on the editor button in the property value field next to “Condition Name”:

 

Hmm – I wonder what that means “Value cannot be null.  Parameter name: activity”.  Is  the Activity is null?  Clearly not – no – the Activity isnt’ null – but the Rule Condition infrastructure (which is invoked when you click on the editor button in the Condition Name property) certainly can’t resolve what Activity this property is part of.  Why?

Well – here is why – its because of an interface known as ISite.  The PropertyGrid, which is the control that is being used to display the properties of the Activity and hence is the container for the classes that help put up the Choose Rule Condition Dialog – aren’t setup correctly. So what has to happen to this sample to make this work?   The PropertyGrid.Site property must be set before the PropertyGrid is used.  ISite is an interface that is used to bind a Component to its Container.  In this case the PropertyGrid relies on ISite to get its services (unlike most other designer components which rely entirely on IServiceProvider).  What is happening is that the internal classes that dispaly the Rule Condition dialogs is trying to find a service and can’t find it because the Site property of the PropertyGrid is null.  So to fix this error, you need to set the PropertyGrid’s Site property.  Any implementation will do – in this case – since the Control that is displaying the worklfow is also the Parent of the PropertyGrid – we can use that Control to provide the implementation of ISite – which is fairly simple.

Here is my simple implementation of this interface for this control :

public partial class WorkflowDesignerControl : UserControl, IDisposable, IServiceProvider,ISite
{
#region ISite Members

IComponent ISite.Component
{
get { return this; }
}

IContainer ISite.Container
{
get { return this.Container; }
}

bool ISite.DesignMode
{
get { return true; }
}

string ISite.Name
{
get
{
return this.Name;
}
set
{
this.Name = value;
}
}

#endregion

#region IServiceProvider Members

object IServiceProvider.GetService(Type serviceType)
{
return this.GetService(serviceType);
}

 

Now – we need to actually set the PropertyGrid’s Site property.  Here is that code from inside of the control’s constructor:


InitializeComponent();
this.propertyGrid.Site = this;

 

After adding that code – the “Select Condition” dialog comes up fine.  Unfortunately if you try to add a new condition via the “New Condition” button – another exception comes up.

Ok – so “Object reference not set to an instance of an object”.  What does that mean?

What is happening here – is that the RuleConditionDialog class is looking for a Service (if you start to play with Designer re-hosting you will quickly become intimate with an interface named IServiceProvider – and the way that different designer component are de-coupled and only communicate via services).

The service it is looking for is ITypeProvider.  ITypeProvider is an interface that is exposed from the workflow assemblies itself.  It is a type that other worklow types (the Rules infrastructure for one – WorkflowMarkupSerializer for another) use to get dynamic type information.  The cool thing about this system is that is become very easy for you as the designer hoster to dynamically give those components types that might not be able to be resovled via the normal .NET assembly and type resolution system.  This is cool, so we need to provide this service to the design environment so that we can get to the next dialog and actually edit a RuleCondtion!

Here again is a simple implemenation to get things moving.

internal class CustomTypeProvider : ITypeProvider
{
public CustomTypeProvider(IServiceProvider sp)
{
_contained = new TypeProvider(sp);
_provider = sp;
Assembly[] assemblyArray1 = AppDomain.CurrentDomain.GetAssemblies();
for (int num1 = 0; num1 < assemblyArray1.Length; num1++)
{
Assembly assembly1 = assemblyArray1[num1];
_contained.AddAssembly(assembly1);
}
}
IServiceProvider _provider;
TypeProvider _contained;
#region ITypeProvider Members

Type ITypeProvider.GetType(string name, bool throwOnError)
{

return _contained.GetType(name, throwOnError);

}

Type ITypeProvider.GetType(string name)
{

Type t = _contained.GetType(name);

return t;
}

Type[] ITypeProvider.GetTypes()
{
Type[] types = _contained.GetTypes();
return types;
}

System.Reflection.Assembly ITypeProvider.LocalAssembly
{
get
{
Assembly a = _contained.LocalAssembly;
return a;
}
}

ICollection<System.Reflection.Assembly> ITypeProvider.ReferencedAssemblies
{
get
{
ICollection<Assembly> asms = _contained.ReferencedAssemblies;
return asms;
}
}

IDictionary<object, Exception> ITypeProvider.TypeLoadErrors
{
get
{
IDictionary<object, Exception> errors = _contained.TypeLoadErrors;
return errors; ;
}
}

event EventHandler ITypeProvider.TypeLoadErrorsChanged
{
add { _contained.TypeLoadErrorsChanged += value; }
remove { _contained.TypeLoadErrorsChanged -= value; }
}

event EventHandler ITypeProvider.TypesChanged
{
add { _contained.TypesChanged += value; }
remove { _contained.TypesChanged += value; }
}

#endregion
}

What does this implementation do?  It just scans the AppDomain for any loaded assemblies and adds those assemblies to the TypeProvider type implemented by the Workflow framework itself, and just delegates calls to the contained TypeProvider.  Once added to the environment (this is typically done inside of the WorkflowDesignerLoader.Initialize method – since that is generally when types need to be found initially).

//inside WorkflowDesignerLoader.Initialize override
host.AddService(typeof(ITypeProvider), new CustomTypeProvider(LoaderHost), true);

Ok – so now – we can actually bring up the RuleConditionDialog which allows us to create a new RuleCondtion.

Whew – that’s alot for tonight – especially this late – I am having to blog lately after my normal work hours on the WF contracting project I am working on.  :)  Part Two of this post (yes I realize I now have two Part Two posts to complete) – will go into how you can actually load and store rules if you are hosting the designer outside of VS.NET.