DependencyProperties the easier way

After working with Workflow Foundation at a customer I had to admit: I hate DependencyProperties. Yes I hate them. They exist because Workflow Foundation, Windows Presentation Foundation and now also Silverlight needed some way to add features to properties. I must admit I would have liked a different approach by using some kind of Aspect Orientation with PostSharp or so. But I still have to make use of them, so let's make the creation of DependencyProperties easier, an Resharper Live Template. Just a few words (Property Name, Property Type, Activity Type and PropertyDescription) and you'll have a full DependencyProperty. I made the Live Template start with word "dprop" and also made the Activity Type be selected by the containing class type.

1 public static DependencyProperty $PROPERTYNAME$Property = 2 DependencyProperty.Register("$PROPERTYNAME$", typeof ($PROPERTYTYPE$), 3 typeof ($ACTIVITYTYPE$)); 4 [Description("$PROPERTYDESCRIPTION$")] 5 [Browsable(true)] 6 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 7 public $PROPERTYTYPE$ $PROPERTYNAME$ 8 { 9 get { return ($PROPERTYTYPE$) GetValue($PROPERTYNAME$Property); } 10 set { SetValue($PROPERTYNAME$Property, value); } 11 }

Have fun with it.

Experiencing Workflow Foundation - Persistence Service and Tracking Service

Just at a new client and investigating if Workflow Foundation will suit their needs. I'm pretty new to Workflow so it's a nice learning step for me. I already stepped into the Persistence and Tracking features of Workflow Foundation.

Combining the Persistence Service and Tracking Service in one Database

It's very easy to add the Persistence Service or the Tracking Service to the WorkflowRuntime. To add the Persistence Service you can configure this like:

<Services> <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" LoadIntervalSeconds="5" /> </Services>

You can add the Tracking Service by doing something very similiar:

<Services> <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </Services>

The next step for me was very simple. At least I thought so, just combining the two services like this, didn't work:

<Services> <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" LoadIntervalSeconds="5" /> </Services>

I didn't really get an Exception of some kind. I even listened to the WorkflowRuntime.ServicesExceptionNotHandled event but nothing happened. It only seemed the Workflow didn't end with Completed but with Aborted. Yes alright but what's wrong? Tell me, please.
So again Google is my friend, and led me to the blog of Bruce Bukovics. So in the end it doesn't seam to be very difficult, it's just something you need to know. Just add the "SharedConnectionWorkflowCommitWorkBatchService" service in the config so you have a combined config like this:

<Services> <add type="System.Workflow.Runtime.Hosting.SharedConnectionWorkflowCommitWorkBatchService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" LoadIntervalSeconds="5" /> </Services>

User Event Tracking

It's possible to do some UserEvent tracking by calling the Activity.TrackData method. This method accepts a key and a value. The Tostring() method is called on the object, so there's no real serialization of the data in the object. We can off course manual serialize the data in an object. So I did some coding to make the Tracking of complex objects possible. I first thought about creating an Extensibility Method for the Activity class, but I won't be able to call the original TrackData method because it's protected. So I have the code that you can add to your custom activity to track the user event data easier.

1 public void TrackObjectData<T>(string userDataKey, T userData) where T : class, new() 2 { 3 this.TrackData(userDataKey, userData.SerializeObject()); 4 }


The code makes use of the following SerializationHelper extension methods.

1 public static class SerializationHelper 2 { 3 public static string SerializeObject<T>(this T obj) where T: class,new() 4 { 5 string result = string.Empty; 6 StringWriter stringWriter = new StringWriter(); 7 try 8 { 9 XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType()); 10 xmlSerializer.Serialize(stringWriter, obj); 11 result = stringWriter.ToString(); 12 } 13 catch 14 { 15 } 16 finally 17 { 18 if (stringWriter != null) 19 stringWriter.Close(); 20 } 21 return result; 22 } 23 public static T DeserializeObject<T>(this string xmlString) where T : class, new() 24 { 25 StringReader reader = new StringReader(xmlString); 26 T deserializedObject = default(T); 27 try 28 { 29 XmlSerializer ser = new XmlSerializer(typeof(T)); 30 deserializedObject = (T)ser.Deserialize(reader); 31 } 32 catch 33 { 34 } 35 finally 36 { 37 reader.Close(); 38 } 39 return deserializedObject; 40 } 41 }


More will follow soon
Besides this I will be looking at a few other features of Workflow Foundation that I will probably blog about. Things like implementing a workflow host and communicating with the workflow from the outside.

Workflow Foundation parameters done better!?

I think a lot of people don't like the way Workflow Foundation parameters work. At least I totally dislike it. I don't want to use an almost untyped (yes, we have object as type) dictionary to read values, and besides why do we need to cast, the types are available. Instead of using basic strings for the parameter naming, I have a different approach. I add an static inner class to the Workflow codebeside.

1 public static class Parameters 2 { 3 public const string BaseFolder = "BaseFolder"; 4 public const string AnalysisResult = "AnalysisResult"; 5 }

When inside the Parameters class I also have a ReSharper Live Template, stpar:

public const string $ParamName$ = "$ParamName$";

In this example I can use the workflow parameter names by using :

WorkflowName.Parameters.BaseFolder

You now have to add a real C# property with the same name as we have in the Parameters class. But much less work, and through this approach we won't make that many mistakes in the string-based key.

Besides this the use of a basic Dictionary isn't that readable too. So I created a somewhat fluent interface for use with Dictionary<TKey,TValue>. So we can create a dictonary more readable than the basic way.

1 FluentDictionary<string, object> parameters = new FluentDictionary<string, object>() 2 .Add(AdministrationAnalysis.Parameters.BaseFolder, @"C:\") 3 .Add(AdministrationAnalysis.Parameters.AnalysisResult, null);

So here''s the code for the FluentDictionary:

1 public class FluentDictionary<TKey, TValue> 2 { 3 private readonly Dictionary<TKey, TValue> internalDictionary = new Dictionary<TKey, TValue>(); 4 5 public FluentDictionary() 6 { 7 } 8 9 public FluentDictionary(Dictionary<TKey, TValue> internalDictionary) 10 { 11 this.internalDictionary = internalDictionary; 12 } 13 14 public Dictionary<TKey, TValue> Dictionary 15 { 16 get { return internalDictionary; } 17 } 18 19 public int Count 20 { 21 get { return internalDictionary.Count; } 22 } 23 24 public FluentDictionary<TKey, TValue> Add(TKey key, TValue value) 25 { 26 internalDictionary.Add(key, value); 27 return this; 28 } 29 30 public void Clear() 31 { 32 internalDictionary.Clear(); 33 } 34 35 public bool ContainsKey(TKey key) 36 { 37 return internalDictionary.ContainsKey(key); 38 } 39 40 public bool ContainsValue(TValue value) 41 { 42 return internalDictionary.ContainsValue(value); 43 } 44 45 public TTyped Get<TTyped>(TKey key) where TTyped : TValue 46 { 47 return (TTyped) internalDictionary[key]; 48 } 49 }

Let me know if you like this approach or if you have a better solution.

Service Oriented Architecture in the Real World

SOA in the Real World is a book that introduces a set of SOA capabilities and explores them. You can download this book for free from the Microsoft site. I think this is the ideal time for me to get to know SOA better and how it can be applied in the Real World.

Sharepoint Workflow Runtime Safe for Escalation Workflow?

When working with Workflow it is possible to add a waitfor activity to build some sort of Escalation mechanism. Very interesting, and often used I would say. But when using Workflow Foundation you must be reminded that Workflow instances will only run when the Worflow Runtime is running.

This is very interesting in standard ASP.NET scenario's. Most of the time you will choose to host your Workflow runtime in one of the ASP.NET processes. At first this doesn't sound like a problem. But have you ever noticed your ASP.NET processes are recycled after some inactive time? This won't happend on a ASP.NET application with heavy traffic, for every minute in a 24-hour day. But a lot of companies have ASP.NET Intranet applications, which are active for some time during the day, and inactive during the night.

When hosting your Workflow Runtime in your ASP.NET process this will make sure your Workflow Runtime will be recycled during the night. So there must be a mechnism to make sure your Workflow Runtime doesn't stop. Because a stopped Workflow Runtime means no running Workflow instances. You can use a Windows Service to leverage a Workflow Runtime, but communication with a Windows Service from inside your ASP.NET application isn't that simple. But we are allowed to make a choice for this, what would we want.

But how does this work in Sharepoint 2007?

I couldn't find an answer to this. At least not directly. I found an article about how to debug your Workflow instance that is hosted by the Sharepoint Workflow Runtime. This article explains how you can debug your Workflows inside Sharepoint. You are instructed to attach your debugger to the w3wp processes, the standard processes in which ASP.NET operates. This asumes to me that the problem described also exists in Sharepoint. But can anyone assure that the Workflow runtime is hosted on the w3wp process?