This is the third part (see part I & part II) of my series on Unity & ObjectBuilder. The previous introductory material was only there to make the stuff described below more accessible and the framework described here is in fact a complete addition to the Unity toolset, it allows you:
Before giving you concrete code samples let me first highlight what I wanted to achieve and why I created this extension in the first place.
During the development of GraphSquare (see this article and the architecture series) I needed a flexible mechanism to convert entities and to let the MVC model pass various stages of transformation to serialize things to a variety of export formats (binary, XML, WCF entities and so on). The idea can be best understood as a combination of ORM and serialization. Imagine that you have some domain entities which need to be converted to data entities through a series of actions like depicted in the picture below. Now, the ‘transaction’ here is a package which combines the start and end result and the ‘workflow’ is a series of ‘actions’ which can be chained and which reacts according to what the transaction state (content) is. Obviously, the transformation to an XML output requires a different set of actions than a binary one, yet there is some overlap.

Each action requires either the whole domain entity or some part of it and maybe even some information from the already created data entities. In addition, writing code to assign all this to each instance becomes a repetitive task. So, the idea quickly became involved and difficult to implement. Until I realized that Unity and ObjectBuilder together with their extensibility model were perfectly adapted to this kind of paradigm.
The following snippet shows a typical import/export which injects the exported stuff into a requesting import property.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Person { [Import("TheAddress")] public Address Address { get; set; } } [Export("TheAddress")] public class Address { public string AddressLine { get; set; } public string Zip { get; set; } public string City { get; set; } } |
Obviously you could achieve the same with the Dependency attribute from Unity with the difference that here an arbitrary string is allowed. But there is more. The following snippet extends the above with a sub-level injection.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Person { [Import("TheAddress")] public Address Address { get; set; } [Import("TheAddress.Zip")] public string TheZipCode { get; set; } } [Export("TheAddress")] public class Address { public string AddressLine { get; set; } public string Zip { get; set; } public string City { get; set; } } |
This mechanism can go as deep as necessary and if a data type or string mismatch occurs the process just stops and the property remains unassigned. Referring to the picture above, this tagging mechanism allows pieces of the transaction to be assigned to actions.
Now, the workflow mechanism require abviously some underlying interface in order to run an action. This can be seen in the snipper below where each action is tagged with the Action attribute which allows action to call each other through a base method implemented as part of that interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
[Export("Transaction")] public class MyTransaction { } [Action("Level3")] public class Level3Action : ActionBase { [Import("Transaction.DataEntity.Name")] public string Message { get; set; } /// /// Runs the action. /// public override void Run() { //whatever } } [Action("Level2")] public class Level2Action : ActionBase { /// /// Runs the action. /// public override void Run() { RunAction("Level3"); } } [Action("Level1")] public class Level1Action : ActionBase { [Import("Transaction.DomainEntity.Name")] public string Message { get; set; } /// /// Runs this workflow. /// public override void Run() { RunAction("Level2"); } } |
The important note here is that everything is handled in the Unity container; the injection of data and running the actions. Calling the workflow is really easy and amounts to the following bits:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void WorkflowTest() { IUnityContainer ctx = new UnityContainer(); ctx.AddNewExtension< ExtensibilityExtension>(); ctx.AddNewExtension< WorkflowExtension>(); ctx.RegisterType< Level1Action>(); ctx.RegisterType< Level2Action>(); ctx.RegisterType< Level3Action>(); ctx.RegisterType< MyTransaction>(); bool ret = ctx.RunAction("Level1"); } |
Of course, the implementation is such that it does not interfere with the registration of named types or instances. That is, you can still use all the standard Unity stuff and register named types even if the export or action name is different.
I have implemented the import/export idea separately from the workflow idea for clarity but merging the code is a blink away.
I’ll first highlight how the import/export stuff is implemented and explain later the ‘action’ or workflow extension.
A Unity extension is created by inheriting from the UnityContainerExtension base class and you can supply in the Initialize method the stuff you want to add to Unity:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class ExtensibilityExtension : UnityContainerExtension { protected override void Initialize() { //add the policy bag which will keep track of the registered exports Context.Policies.Set< IExportPolicy>(new ExportPolicy(), null); Context.Strategies.AddNew< ExtensibilityStrategy>(UnityBuildStage.Initialization); //hook on the registration events Context.Registering += (o, e) => ParseAttributes(e.TypeTo ?? e.TypeFrom, null); Context.RegisteringInstance += (o, e) => ParseAttributes(e.Instance.GetType(), e.Instance); } |
If you want to add a lifetime container, some policies or strategies then this is the place to register them through the ExtensionContext. The ExportPolicy is the policy which will keep the type-name coupling inside Unity. Indeed, the export attribute in essence creates a binding between a Type and a string name. Unity allows you to either register a type or an instance. In the first case we’ll simply store the type-name pair while in the second we’ll also store the instance for future reference.
The Context has two events (Registering and RegisterInstance) which are raised when either a type respectively an instance is registered. Note that these events have nothing to do with strategies or the building process. So, when these events are raised we parse the type (or the instance) for possible ExportAttribute occurence. This parsing is explained in part II of this series and I’ll refrain for repeating myself here. The net result now is that if a type/instance is registered with an Export attribute then the type/name/instance is stored in the IExportPolicy container and whenever the build process needs to check a (export) name the this container is the place to look for. How? Simply by adding a strategy, the ExtensibilityStrategy. This strategy will parse the requested type/instance for Import attributes and look up the IExportPolicy container for the corresponding data, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
private void FindImports(IBuilderContext context, object buildKey, object existing) { if (existing == null) { return; } Type t = BuildKey.GetType(buildKey); foreach (PropertyInfo propertyInfo in t.GetProperties()) { ImportAttribute[] attrs = (ImportAttribute[])propertyInfo.GetCustomAttributes(typeof(ImportAttribute), true); if (attrs.Length > 0) { ImportAttribute impat = attrs[0]; if (string.IsNullOrEmpty(impat.ImportedName))//name is not assigned { throw new ApplicationException(string.Format("Type '{0}' has an Import attribute but without a name.", t.Name)); } Debug.WriteLine(string.Format("Type '{0}' needs an exported type with name '{1}'", t.Name, impat.ImportedName)); IExportPolicy apol = context.Policies.Get< IExportPolicy>(null); TypedInstance ti = apol.Get(impat.ImportedName); if (ti != null) { object tobeassigned = ti.Instance; if (tobeassigned == null) { IBuilderContext clone = context.CloneForNewBuild(new NamedTypeBuildKey(ti.Type, ""), null); clone.Strategies.ExecuteBuildUp(clone); tobeassigned = clone.Existing; } object currentObject = tobeassigned; if (impat.ImportedSubName != null) { for (int i = 0; i < impat.ImportedSubName.Length; i++) { try { string currentMemberName = impat.ImportedSubName[i]; PropertyInfo minfo = currentObject.GetType().GetProperty(currentMemberName); currentObject = minfo.GetGetMethod().Invoke(currentObject, null); } catch (Exception) { break; } } } if (propertyInfo.PropertyType.IsAssignableFrom(currentObject.GetType())) propertyInfo.GetSetMethod().Invoke(existing, new[] { currentObject }); } else { //The import could not be found! //you can either gracefully notify the user or throw an exception //We will silently ignore the missing import and let the application //logic raise an exception when the property is being used. } } } } } |
Note in particular the iteration over sub-names which allows you to fetch parts of (registered) objects.
And this is it, except for a few trivial attribute and utility classes declarations. Now, for the workflow/action idea one just repeats the same story but there is a twist. Let me explain.
You will find in the sample code the following extension method for the IUnityContainer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static bool RunAction(this IUnityContainer ctx, string actionName) { try { IAction action = ctx.Resolve< IAction>(actionName); if (action != null) action.Run(); else return false; } catch (Exception) { return false; } return true; } |
which allows you to run an action on the basis of a name. This works well outside the container because you can call the Resolve method, but how to achieve a chaining of action from inside the container? The easiest way is by means of an event, as can be seen in the definition of the IAction interface:
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface IAction { /// /// Occurs when an with the name specified is requested (inline chained in the workflow). /// event EventHandler< ActionArgs> OnAction; /// /// Runs the action. /// void Run(); } |
This event is being parsed by the PostBuildUp method of the strategy like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public override void PostBuildUp(IBuilderContext context) { //let's hook on the action event if (context.Existing != null && (context.Existing is IAction)) { IAction action = context.Existing as IAction; action.OnAction += (o, e) => RunAction(context, e.ActionName); } } private static void RunAction(IBuilderContext context, string actionName) { //let's resolve the action and iterate the process IBuilderContext clone = context.CloneForNewBuild(new NamedTypeBuildKey(typeof(IAction), actionName), null); clone.Strategies.ExecuteBuildUp(clone); IAction action = clone.Existing as IAction; if (action != null) action.Run(); } |
where we have used the iteration trick explained in the previous article of this series to instantiate a chained action.
While all this might look a little daunting at first, the conceptual path is really straightforward and builts on top of the stuff we explained in part I & part II.
I tried to comment as much as possible the code and I hope this series of articles were helpful. You can download the code with a sample console application and I’d hope you find it easier now to build flexible applications on the basis of Unity and develop your own extensions.
Some (self-indulgent) overview of G2's interface hierarchy is available.
Read moreThe Enterprise Library 4.1 was released together with Unity 1.2 in which a whole interception mechanism was added as part of the aspect-oriented approach to coding and a merge of the policy injection ideas. This little note is just a stepping stone if you wish to play with the new interception namespace.
Read more
Great series, thanks
By dave December 11, 2009 - 12:58 amQuick one for you: using NInject, I’ve been able to register types like this:
IService
{
void Configure(XmlNode configuration);
TContract GetImplementation();
}
Then, I would define my services in the configuration file, with an optional configuration node, and at run-time, my ApplicationManager would read the configuration, instantiate the services, apply any configuration, and store it in the container, keyed by the contract (TContract) type. At run-time, I would do: container.Get(). Then, if the resolved type was of type IService, the system would automatically call GetImplementation, leaving the lifetime management be the responsibility of the service itself.
That’s fairly useful for a WCF proxy. I would configure the service with an endpoint name, and I would simple call the container to resolve the type of the contract, and get a fresh ChannelFactory().CreateChannel() channel each time, because of the call to GetImplementation. If I prefered to register a mock instead, then I would simple implement TContract on a mock class, and register that in the container.
I’m trying to achieve the same thing with Unity, since one of my clients uses Prism as their composite application framework. Here’s the sequence:
1- Instantiate a type of type IService where TContract is IContract, for example. Put that in the “service” variable.
2- Register that instance, something like that: container.RegisterInstance(service). => that doesn’t work! service doesn’t implement IContract, but IService!!
3- During the resolve phase, automatically call GetImplementation on the service, and return that.
Any insight would be greatly appreciated! Thanks.
By dave December 11, 2009 - 1:17 amdamn, I should’ve html encoded my comment… it removed all the <TContract> from my previous comment…
instead of IService, you should read IService<TContract>. Sorry about that…
By dave December 11, 2009 - 1:19 amlet me rephrase it
It’s a good thing that you moderate the comments.
Quick one for you: using NInject, I’ve been able to register types like this:
IService<TContract>
{
void Configure(XmlNode configuration);
TContract GetImplementation();
}
Then, I would define my services in the configuration file, with an optional configuration node, and at run-time, my ApplicationManager would read the configuration, instantiate the services, apply any configuration, and store it in the container, keyed by the contract (TContract) type. At run-time, I would do: container.Get<IContract>(). Then, if the resolved type was of type IService<TContract>, the system would automatically call GetImplementation, leaving the lifetime management be the responsibility of the service itself.
That’s fairly useful for a WCF proxy. I would configure the service with an endpoint name, and I would simply call the container to resolve the type of the contract, and get a fresh ChannelFactory().CreateChannel() channel each time, because of the call to GetImplementation. If I prefered to register a mock instead, then I would simple implement TContract on a mock class, and register that in the container.
I’m trying to achieve the same thing with Unity, since one of my clients uses Prism as their composite application framework. Here’s the sequence:
1- Instantiate a type of type IService<TContract> where TContract is IContract, for example. Put that in the “service†variable.
2- Register the service instance, something like that: container.RegisterInstance<IContract>(service). => that doesn’t work! service doesn’t implement IContract, but IService<IContract>!!
3- During the resolve phase, automatically call GetImplementation on the service, and return that.
Any insight would be greatly appreciated! Thanks.
By dave December 11, 2009 - 1:27 amSent you a mail regarding this, check your box Dave.
By Francois December 11, 2009 - 8:46 am