Unity & ObjectBuilder (part II)

In the first part of this series I gave you an overview of Unity and OB. In this article I have picked out some concrete examples which cover the various (technical) ideas and OB techniques. While Unity & OB can be overwhelming at first in the amount of interface and classes you are presented with, I hope these articles demonstrate that if you keep a map of the main roads in your mind it’s quite safe (and fun!) to walk around in uncharted OB areas.

In the first part of this series I gave you an overview of Unity and OB. In this article I have picked out some concrete examples which cover the various (technical) ideas and OB techniques. While Unity & OB can be overwhelming at first in the amount of interface and classes you are presented with, I hope these articles demonstrate that if you keep a map of the main roads in your mind it’s quite safe (and fun!) to walk around in uncharted OB areas.

How to make a singleton?

An object is being made through a sequence of strategies, so if you wish to return an existing instance every time a particular (singleton) type is requested you only need to cache it the first time it’s made and shortcut the creation on every subsequent request. In addition, because the PreBuildUp is called during the top-down loop you need to put this shortcut strategy in the head of the chain.

Warning! The implementation here is for demonstration purposes, see the end of this section for the reason it’s not suitable in general.

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
70
71
public void Basic()
{
    Builder builder = new Builder();
    Locator locator = new Locator();
    LifetimeContainer lifetime = new LifetimeContainer();
    PolicyList policies = new PolicyList();
    StrategyChain chain = new StrategyChain();
    chain.Add(new SingletonStrategy());
    chain.Add(new FirstStrategy());
    chain.Add(new SecondStrategy());
    object firsttime = builder.BuildUp(locator, lifetime, policies, chain, typeof(MyClass), null);
    object secondtime = builder.BuildUp(locator, lifetime, policies, chain, typeof(MyClass), null);
    Assert.AreEqual(firsttime, secondtime);
 
}
 
class FirstStrategy : BuilderStrategy
{
    public override void PreBuildUp(IBuilderContext context)
    {
        Debug.WriteLine("First strategy, prebuilding " + context.OriginalBuildKey);
        if (context.BuildKey.Equals(typeof(MyClass)))
        {
            MyClass c = new MyClass();
            context.Existing = c;
            context.BuildComplete = true;
        }
    }
}
private class SecondStrategy : BuilderStrategy
{
    public override void PreBuildUp(IBuilderContext context)
    {
        Debug.WriteLine("Second strategy, prebuilding " + context.OriginalBuildKey);
        base.PreBuildUp(context);
    }
    public override void PostBuildUp(IBuilderContext context)
    {
        Debug.WriteLine("Second strategy, postbuilding " + context.OriginalBuildKey);
        base.PostBuildUp(context);
    }
}
private class MyClass
{
 
}
internal class SingletonStrategy : BuilderStrategy
{
    public override void PreBuildUp(IBuilderContext context)
    {
        //if the singleton was created before, return it
        if (context.BuildKey.Equals(typeof(MyClass)))
        {
            MyClass c = context.Locator.Get< MyClass >("MySingletonClass");
            if (c != null)
            {
                context.Existing = c;
                context.BuildComplete = true;
            }
        }
    }
 
    public override void PostBuildUp(IBuilderContext context)
    {
        //if another strategy created the singleton, let's cache it
        if (context.BuildKey.Equals(typeof(MyClass)) &amp;&amp; context.Existing != null)
        {
            context.Locator.Add("MySingletonClass", context.Existing as MyClass);
        }
    }
}

The logic is really easy, you create a little strategy which checks the existence of a cached object with key “MySingletonClass” in the Locator. The PreBuildUp method returns the object if it exists and this method is called in the beginning of the build process. The PostBuildUp is called at the end of the build process after all the other strategies, so if any of them has created the instance then we can cache it in the Locator for the next time. Piece of cake.

This code works fine but has a drawback; if you want to make this code generic (in a non-C# sense) then the line with the type check at the beginning of the build methods is impossible. One cannot create a singleton strategy for each type. In addition, the Locator should have more structure to organize multiple singletons. The solution is to use a policy with a singleton strategy. Instead of checking the Locator by means of a string-key, you use the key combination ‘BuildKey – LifeTimeKey’ in the policy collection and if it exists the this will be the container for the singleton instance.

Let me guide you a little through the Unity implementation. Unity implements most of its behavior through a default extension, the UnityDefaultStrategiesExtension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UnityDefaultStrategiesExtension : UnityContainerExtension
{
    /// <summary>
    /// Add the default ObjectBuilder strategies &amp; policies to the container.
    /// </summary>
    protected override void Initialize()
    {
        //
        // Main strategy chain
        //
        Context.Strategies.AddNew< buildkeymappingstrategy >(UnityBuildStage.TypeMapping);
        Context.Strategies.AddNew< lifetimestrategy >(UnityBuildStage.Lifetime);
        //much more...
     }
 
     //much more...
}

The BuildKeyMappingStrategy is just a fancy mapping action which allows you to map a type to another on the fly. Obvisouly, this one needs to be there before anything else. Next, as explained above, there the the lifetime strategy which in Unity is associated either to a TransientLifetimePolicy of to a SingletonLifetimePolicy. They both implement the ILifetimePolicy but only the singleton one stores something, the transient one acts as a dummy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LifetimeStrategy : BuilderStrategy
{
...
public override void PreBuildUp(IBuilderContext context)
{
    if(context.Existing == null)
    {
        ILifetimePolicy lifetimePolicy = GetLifetimePolicy(context);
 
        object existing = lifetimePolicy.GetValue();
        if (existing != null)
        {
            context.Existing = existing;
            context.BuildComplete = true;
        }
    }
}
...
}

When the PreBuildUp is called on the LifetimeStrategy the code checks if there is a lifetime policy. As alluded above, the key-combination BuildKey/ILifetimePolicy is used to fetch this policy object. Now, underneath this check there will always be a policy, either a transient or a singleton, but one will return NULL on the GetValue() call while the singleton returns the single instance. If this instance is not NULL the Existing is set and BuildComplete is true.

If you compare my naive implementation with the Unity one you will notice that the cache in my implementation is replaced by some smarter calls to the Policies collection, but conceptually the idea is the same.

How to build while building?

Before moving on and showing you how to tell OB to use attributes, let me first show you how you can recurse the building process inside OB. Indeed, imagine you are inside a certain strategy and you need to build an object through the same pipeline, i.e. the same IBuilderContext. For this purpose the context has a special method called CloneForNewBuild(). This method will clone the context in which you are and you can start building another type by calling the Strategies.ExecuteBuildUp() method. In fact, one could use this to drop altogether an active build and start another one, returning another type than the one requested.
In the code below I create a simple strategy which builds a MyClass type inside which another type is assigned through a public property. Now, of course, one could simply instantiate the ‘AnotherClass’ type but this would not work if the type is also expected to be part of a OB building process! So, one inevitably depends on going through the same strategy chain and this is were the cloning of context comes in.
Note that I have delegated the build process of ‘AnotherClass’ to a policy and you should be used by now to the idea that policies are the common way to decouple things inside OB.

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
70
71
72
73
74
75
public void Basic2()
{
    Builder builder = new Builder();
    Locator locator = new Locator();
    LifetimeContainer lifetime = new LifetimeContainer();
    PolicyList policies = new PolicyList();
    //add the policy which is required to build the MyClass type.
    policies.Set< IAnotherClassPolicy >(new GetMeAnotherClassPolicy(), typeof(MyClass));
    StrategyChain chain = new StrategyChain();
    chain.Add(new FirstStrategy());
    object theobject = builder.BuildUp(locator, lifetime, policies, chain, typeof(MyClass), null);
    Assert.IsNotNull(theobject);
    Assert.IsNotNull((theobject as MyClass).Another);
}
class FirstStrategy : BuilderStrategy
{
    public override void PreBuildUp(IBuilderContext context)
    {
        //imagine that passing through this method is the 
        //only way to create the type AnotherClass
        if (context.BuildKey.Equals(typeof(AnotherClass)))
        {
            context.Existing = new AnotherClass();
            context.BuildComplete = true;
            return;
        }
 
        if (context.BuildKey.Equals(typeof(MyClass)))
        {
            MyClass c = new MyClass();
            IAnotherClassPolicy pol = context.Policies.Get< IAnotherClassPolicy >(typeof(MyClass));
            if (pol != null)
            {
                c.Another = pol.Get(context);
            }
            context.Existing = c;
            context.BuildComplete = true;
        }
    }
}
 
private class MyClass
{
    public AnotherClass Another { get; set; }
}
 
public class AnotherClass
{
 
}
public interface IAnotherClassPolicy : IBuilderPolicy
{
    AnotherClass Get(IBuilderContext context);
}
public class GetMeAnotherClassPolicy : IAnotherClassPolicy
{
    private AnotherClass singleton;
 
    public AnotherClass Get(IBuilderContext context)
    {
        if (singleton != null)
        {
            return singleton;
        }
        //the following line recurse the build process
        IBuilderContext ctx = context.CloneForNewBuild(typeof(AnotherClass), null);
        object newcreated = ctx.Strategies.ExecuteBuildUp(ctx);
        if (newcreated != null && (newcreated is AnotherClass))
        {
            singleton = newcreated as AnotherClass;
            return singleton;
        }
        return null;
    }
}

How to use attributes?

This sample is a continuation of the previous one. Instead of explicitly assigning a class instance to a property of a type under construction, wouldn’t it be nice if you could decorate the to-be-created class with stuff that needs to be assigned? In fact, this is for example one of the features (the so-called Import) of the Extensibility Framework, the other half of the idea being the possibility to export a certain type.

To be more concrete, let’s assume we wish to construct a type ‘MyClass’ in which a property should be injected with another class like this:

1
2
3
4
5
6
7
8
9
10
public class MyClass
{
    [Import("MyVerySpecialClass")]
    public AnotherClass Another { get; set; }
}
[Export("MyVerySpecialClass")]
public class AnotherClass
{
 
}

This mechanism is actually more powerful if you use interfaces, in which case you decouple the type binding between the property and the exported type.

Now, I’m not going to explain how to create custom attribute, much less the associated reflection mechanisms which you can look up elsewhere. What I will do is, however, move slowly to a more encapsulated form of OB in which you will recognize the Unity-like shape. In fact, the code below demonstrates the following creation process:

1
2
3
4
5
6
7
8
public void Basic4()
{
    //our pseudo-extensibility-unity container
    ImpexContainer ctx = new ImpexContainer();
    ctx.RegisterType< AnotherClass >();
    MyClass c = ctx.Resolve< MyClass >();
 
}

The ImpexContainer (for import-export) packages the OB stuff and allows you to register type which might have an export definition. If so, the name-type is saved into a policy for later use. If a type (the MyClass type in our case) needs to be build and contains an import, we will call the export map and inject it during the build process.

The attribute definitions amount to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ImportAttribute : Attribute
{
    public string Name { get; set; }
    public ImportAttribute(string name)
    {
        Name = name;
    }
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ExportAttribute : Attribute
{
    public string Name { get; set; }
    public ExportAttribute(string name)
    {
        Name = name;
    }
}

and the policy is really just a dictionary 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
public interface IExportPolicy : IBuilderPolicy
{
    void Add(NamedType namedType);
    Type Get(string name);
}
public class ExportPolicy : IExportPolicy
{
    Dictionary< string, Type > map = new Dictionary< string, Type >();
 
    public void Add(NamedType namedType)
    {
        map.Add(namedType.Name, namedType.Type);
    }
 
    public Type Get(string name)
    {
        if (map.ContainsKey(name))
        {
            return map[name];
        }
        return default(Type);
    }
}

The registration consists of a simple reflection on the type being registered and if the export attribute is assigned we keep the binding in a simple NamedType instance. Solving a type, on the other hand, is just a call to the builder which will involve the appropriate strategy:

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
70
71
72
73
public class ImpexContainer
{
    #region Fields
    Builder builder = new Builder();
    Locator locator = new Locator();
    LifetimeContainer lifetime = new LifetimeContainer();
    PolicyList policies = new PolicyList();
    StrategyChain chain = new StrategyChain();
    IBuilderContext context;
    #endregion
    #region Constructor
    public ImpexContainer()
    {
        //this policy will keep the exports
        //Note that we set the buildkey to null to accept any type
        policies.Set< IExportPolicy >(new ExportPolicy(), null);
        chain.Add(new BuildAndImportStrategy());
    }
    #endregion
    #region Methods
 
    public T Resolve< T >()
    {
        //the type could itself be an export
        //so you could call the RegisterType< T >()
        //here as well, but we'll keep it simple
        return new Builder().BuildUp< T >(locator, lifetime, policies, chain, typeof(T), null);
    }
 
    public void RegisterType<t>()
    {
        NamedType nt = DiscoverExport< T >();
 
        if (nt != null)
        {
            IExportPolicy pol = policies.Get< IExportPolicy >(null);
            pol.Add(nt);
        }
        //if it doesn't export anything we forget about the type
        //but in a real-world scenario you shouldn't, cfr. the actual Unity code
    }
    private NamedType DiscoverExport< T >()
    {
        Type t = typeof(T);
 
        ExportAttribute[] attrs =
            (ExportAttribute[])t.GetCustomAttributes(typeof(ExportAttribute), true);
 
 
        if (attrs.Length > 0) //can be at most one due to the attrib constraint set
        {
            ExportAttribute att = attrs[0];
            return new NamedType(att.Name, t);
        }
        return null;
    }
 
    #endregion
 
 
}
public class NamedType
{
    public string Name { get; set; }
    public Type Type { get; set; }
 
    public NamedType(string name, Type type)
    {
        Name = name;
        Type = type;
    }
}
</t>

To conclude, we need a little recipe in the form of a strategy to inspect a type if it needs imports/injection. Here again, we rely on a policy to look up the mapping or type to be injected. At this place you could call another build process inside the container but we have chosen for a simple instantiation. Note though that the instantiation is done through the Activator object because we only have a type definition through the export. The best part of this story is that you don’t need any casting when the import is being assigned. I do admit that my code is lacking exception handling and soft tissue in case things go bad, but it would blur the essence of what I try to convey.

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
public class BuildAndImportStrategy : BuilderStrategy
{
    public override void PreBuildUp(IBuilderContext context)
    {
        if(context.BuildKey.Equals(typeof(MyClass)))
        {
            MyClass instance=new MyClass();
            //check whether this type has imports
            DiscoverImports< MyClass >(instance, context);
            context.Existing = instance;
            context.BuildComplete = true;
        }
    }
    private void DiscoverImports< T >(T instance, IBuilderContext context) where T : class
    {
        Type t = typeof(T);
        foreach (PropertyInfo propertyInfo in t.GetProperties())
        {
 
            ImportAttribute[] attrs =
                (ImportAttribute[])propertyInfo.GetCustomAttributes(typeof(ImportAttribute), true);
 
            if (attrs.Length > 0)
            {
                ImportAttribute ia = attrs[0];
                IExportPolicy pol = context.Policies.Get<iexportpolicy>(null);
                if (pol != null)
                {
                    //fetch the type corresponding to the name
                    Type importType = pol.Get(ia.Name);
                    //here you could use the trick explained earlier and recurse the build process by cloning 
                    //the context, but again...let's keep things simple for demonstration purposes
                    object tobeassigned = Activator.CreateInstance(importType);
                    //finally, assign the instance to the property
                    //note that we don't even have to cast the activated instance to the type of the property!
                    propertyInfo.GetSetMethod().Invoke(instance, new[] {tobeassigned});
                }
            }
        }
 
    }
}
</iexportpolicy>

At this point you should see two things coming up:

  • the possibility to invent your own extensibility framework
  • how Unity is packaging a lot of useful code managing locators, instantiation, attribute catching and so on

In fact, I’d hope you can go your own way from here and the possibilities are endless. In the next part of this series I will show you a complete sample (a Unity extension actually) which combines all the ideas from above into one coherent mechanism.

Tagged with:
 

4 Responses to “Unity & ObjectBuilder (part II)”

  1. Link collection…

    Link collection…

  2. [...] is the third part (see part I & part II) of my series on Unity & ObjectBuilder. In fact, the previous introductory material was only [...]

  3. [...] the next part I’ll give you plenty of OB examples and do some wicked stuff in order to convince you that [...]

  4. [...] Francois ‘Swa’ M.Vanderseypen führte. Er beschreibt hier schön in einem Artikel (Teil 1, Teil 2) wie Unity aufgebaut [...]

Leave a Reply