The Durable Task Framework, created by Abhishek Lal over at Microsoft, is fantastic. It allows you to leverage the Windows Azure Service Bus to create long running, durable workflows (orchestrations) comprised of one or more sub-tasks (activities) or even sub-workflows. If you’re not familiar with the framework, take a look at it first before continuing. The above link contains a download with some documentation and samples for using the framework.
It didn’t take long for me to get up and running with an orchestration and a few tasks, although I quickly realised that I had overlooked one key point. The input and output objects for each task are serialized and transmitted over the Azure Service Bus. Initially my inputs were complex types with a unity container reference so I could share a single instance of a unity container with all of my orchestrations and tasks. Serializing the unity container is out of the question, but it wasn’t obvious to me how I could use the framework to allow me to inject my unity container in to the orchestrations and tasks.
In this post, I present a solution to configuring your setup such that you can provide your task orchestrations and activities with whatever extra data you need.
After getting in touch with Abhishek Lal, he gave me some pointers on how I could leverage the framework to include unity and it was a relatively simple process. Another big win for the framework! Even though I’m using a unity container in this example, you can use the same principle with whatever you want. Also bare in mind that this is a very trivial example to show the basic principles. Check out the framework samples for more complete and in depth examples.
What we’re going to do here is create a factory for our orchestrations and activities such that during their initialisation we can hand them a unity container instance. This means that if you have a worker role hosting your hub client and worker, you can use the same unity container instance throughout. Pretty neat!
ObjectCreator
Abhishek had the good sense to add an overload to the methods where we add orchestrations and tasks, allowing you to pass in either the type for the orchestration / task activity OR an instance of an ObjectCreator. This is a factory for creating your tasks and orchestrations. We’ll make use of the following methods on the TaskHubWorker:
public TaskHubWorker AddTaskOrchestrations(params ObjectCreator<TaskOrchestration>[] taskOrchestrationCreators); public TaskHubWorker AddTaskActivities(params ObjectCreator<TaskActivity>[] taskActivityCreators);
So what does the ObjectCreator
class look like?
public abstract class ObjectCreator<T> : INameVersionInfo { protected ObjectCreator(); public string Name { get; protected set; } public string Version { get; protected set; } public abstract T Create(); }
A pretty simple class to work with. Here we’re really interested in the Create()
method as this is where we can manipulate the object we’re creating and pass it our unity container.
The class has a single generic type parameter. Looking at those two TaskHubWorker
methods above, this has to be either a TaskOrchestration
or a TaskActivity
. We don’t want to have to create a separate ObjectCreator
implementation for each (although you might want to in your case), so let’s make use of the generic parameter in our implementation.
If you take a peek at the internals of the Durable Task Framework, we can find some example ObjectCreator
implementations, namely the DefaultObjectCreator
. This class is internal so we can’t just subclass it, but we can save ourself some time by using this as a starting point.
namespace Microsoft.ServiceBus.DurableTask { internal class DefaultObjectCreator<T> : ObjectCreator<T> { private Type prototype; private T instance; public DefaultObjectCreator(Type type) { this.prototype = type; this.Initialize((object) type); } public DefaultObjectCreator(T instance) { this.instance = instance; this.Initialize((object) instance); } public override T Create() { if (this.prototype != (Type) null) return (T) Activator.CreateInstance(this.prototype); else return this.instance; } private void Initialize(object obj) { this.Name = Utils.GetDefaultName(obj); this.Version = Utils.GetDefaultVersion(obj); } } }
Pretty simple hey! And very easy to see what’s going on. All we need to do is create a similar implementation and add some code to the Create()
method. But first, how do we know that our target type (T) requires a unity container? To keep things simple, we’ll use an interface that exposes a property for an IUnityContainer
and implement it on our orchestrations and tasks. We’ll call this IUnityContainerObject
.
namespace UnityDurableTaskFramework { using Microsoft.Practices.Unity; public interface IUnityContainerObject { IUnityContainer UnityContainer { get; set; } } }
Its usage:
public class BankingOrchestration : TaskOrchestration<bool, Transaction>, IUnityContainerObject { // ... } public abstract class UnityContainerTaskActivity<TInput, TResult> : TaskActivity<TInput, TResult>, IUnityContainerObject { // ... }
I’ve created a base class to use for any TaskActivity
that requires a unity container to save having to add that property all over the place.
Now we can look for this when we create our objects in the UnityObjectCreator
, a sub-class of ObjectCreator
.
namespace UnityDurableTaskFramework { using Microsoft.Practices.Unity; using Microsoft.ServiceBus.DurableTask; using System; using System.Dynamic; using System.Reflection; public class UnityObjectCreator<T> : ObjectCreator<T> where T : class { private Type prototype; private T instance; private IUnityContainer unityContainer; public UnityObjectCreator(IUnityContainer unityContainer, Type type) { this.unityContainer = unityContainer; this.prototype = type; this.Initialize((object)type); } public UnityObjectCreator(IUnityContainer unityContainer, T instance) { this.unityContainer = unityContainer; this.instance = instance; this.Initialize(instance); } public override T Create() { T result; if (this.instance == null) { result = Activator.CreateInstance(this.prototype) as T; } else { result = this.instance; } var unityOrchestration = result as IUnityObject; if (unityOrchestration != null) { unityOrchestration.UnityContainer = this.unityContainer; } return result; } private void Initialize(object obj) { this.Name = GetDefaultName(obj); this.Version = string.Empty; } private string GetDefaultName(object obj) { if (obj == null) { throw new ArgumentNullException("obj"); } string str = string.Empty; Type type; MethodInfo methodInfo; InvokeMemberBinder invokeMemberBinder; return !((type = obj as Type) != (Type)null) ? (!((methodInfo = obj as MethodInfo) != (MethodInfo)null) ? ((invokeMemberBinder = obj as InvokeMemberBinder) == null ? obj.GetType().ToString() : invokeMemberBinder.Name) : methodInfo.Name) : type.ToString(); } } }
I’ve added a ‘class’ constraint on the generic type parameter here to allow us to use as
in the Create()
method rather than an explicit cast. All we want to create here are instances of TaskOrchestration
and TaskActivity
, so we’re not imposing any restrictions on ourselves. It would be really nice to add another constraint for the IUnityContainerObject
interface, but due to the type hierarchy (and no covariance on the generic type parameter) we’re not able to do that just yet. No biggie though, just an extra ‘if’.
Usage
So how do we use our new factory?
taskHub.AddTaskOrchestrations( new UnityObjectCreator<TaskOrchestration>(unityContainer, typeof(BankingOrchestration))); taskHub.AddTaskActivities( new UnityObjectCreator<TaskActivity>(unityContainer, typeof(CreditAccountTask)), new UnityObjectCreator<TaskActivity>(unityContainer, typeof(DebitAccountTask)));
The attached source code provides a working sample of this in action. The sample creates a very simple bank transaction processor that debits an amount of money from one account to another. The debiting and crediting tasks are separated out and use a unity container to gain access to the bank account.
The code is available for download as a Visual Studio 2012 solution, built against .Net 4.5 and ServiceBus.DurableTask Framework 0.2.0.2.
Note: To make this work, you’ll need an Azure subscription and a service bus connection string. Edit the app.config in the project to set your subscription connection string. At present, there isn’t any way to emulate the service bus locally.
Also make sure you have the NuGet package manager installed. The source archive doesn’t contain all the dependencies to save on storage space, but when you first build the project they should be downloaded automatically.
[…] my last post, Using Unity With The Durable Task Framework, I mentioned that I had originally overlooked the fact that the data passed to and from your […]
[…] SetupTaskHub method is the lengthiest method of the class, but from my first post in the series, should be familiar with the addition of some generic type parameters which are the […]