A continuing series from the previous article here
The latest version of this code is now hosted at Codeplex
Editor Note 02/16/2008: The P&P team has released the first CTPs for DI container named Unity. This is not dissimilar to the approach that is discribed in this series of postings. We suggest that the code here is used only as a learning exercise for Object Builder and Unity itself is used for your applciations.
Selecting a Constructor
So far through this series we've dealt with pretty straight forward objects. Each has had only one constructor from which to pick from for constructing a type. But not all our classes are so straight forward in a real solution. So the questions are: how does Object Builder select a constructor by default and how can I select a different constructor? Well the easiest way to explore this is via a test
Let's postulate the following type:
public class MyObject
{
private Object parameter;
public MyObject()
{
Console.WriteLine("Default Constructor Called");
}
public MyObject(Object value)
{
Console.WriteLine("Parameterized Constructor Called");
this.parameter = value;
}
public Object Parameter { get { return this.parameter; } }
}
So which constructor is called?

The smallest constructor.
Rule #1: Object Builder is not a greedy builder meaning it takes the easiest (as determined by number of parameters) constructor it can find by default.
Calling a Non-Default Constructor
So how do we call the other constructor? Well if you said 'via a policy' you'd be correct. Specifically we'll use the ConstructorPolicy type. The class exposes 3 constructors, two of which are going to be of interest to us, and specifically the first:
ConstructorPolicy(ConstructorInfo, params IParameter);
ConstructorPolicy(params IParameter);
The secret to this policy is that it accepts a set of IParameter instances that can be applied in constructing a type. In the first prototype, it applies the list of parameters to the supplied ConstructorInfo. In the second, it attempts to find the appropriate constructor based on the supplied parameter list. In order to use the ConstructorPolicy simply supply a configured instance to the Builder in your code like this:
//Assumes that variable builder is an initialized instance of the Builder type
//Assumes that variable policy is an initialized instance of the ConstructorPolicy type
builder.Policies.Set <ICreationPolicy> (policy, typeof (MyObject), null);
Pretty easy stuff...
The real art of the system is in using the IParameter types to get the functionality you want. Baked into the library are the following implementations:
* All these types are located in Microsoft.Practices.ObjectBuilder namespace
** The CreationParameter, DependencyParameter, ValueParameter/ValueParameter<T> types are all subclassing peers
This is the "bread'n'butter" class for constructor building with ObjectBuilder. In a nutshell it tells a Builder to use the capabilities of ObjectBuilder itself to create the constructor parameter for the type it is creating. Read: it exhibits reentrantancy for object build up operations. This is the most intuitive of the IParameter types as the policies put in place for the dependencies automatically are applied here. i.e. If MyObject depends on a type of Object in a parameter and the type Object has been configured with a TypeMappingPolicy of MyOtherObject, ObjectBuilder will create an instance of MyOtherObject to fulfill the dependency on Object. Any other policies configured for the type will take effect as well, such as the SingletonPolicy.
//Assumes variable constructor is appropriate ConstructorInfo instance
//Let's tell ObjectBuilder to create MyOtherObject in place of Object
TypeMappingPolicy typeMapping = new TypeMappingPolicy(typeof (MyOtherObject), null);
builder.Policies.Set <ITypeMappingPolicy>(typeMapping, typeof (Object), null);
//Let's now set up the ConstructorPolicy
IParameter p = new CreationParameter(typeof(Object));
ConstructorPolicy policy = new ConstructorPolicy(constructor, p);
builder.Policies.Set <ICreationPolicy> (policy, typeof (MyObject), null);
//Create our MyObject instance
MyObject createdType = (MyObject)builder.BuildUp(locator, typeof(MyObject), null, null);
//createdType had the mapped type, MyOtherObject
Assert.IsInstanceOfType(typeof(MyOtherObject), createdType.Parameter);
As you can see, this approach allows us to use our configuration for ObjectBuilder recursively to create all the needed types and associations. I'll call this a simple case of dog-fooding. :)
This is the next most common used parameter type (IMHO). The constructor prototype is:
DependencyParameter(parameterType, objectName, createType, NotPresentBehavior, SearchMode);
The easiest way to explain how it is used is simply to show the code in action.
//Assumes variable constructor is appropriate ConstructorInfo instance
//Let's now set up the ConstructorPolicy
IParameter p = new DependencyParameter(typeof(Object), null, typeof(MyOtherObject), NotPresentBehavior.CreateNew, SearchMode.Local);
ConstructorPolicy policy = new ConstructorPolicy(constructor, p);
builder.Policies.Set <ICreationPolicy> (policy, typeof (MyObject), null);
//Create our MyObject instance
MyObject createdType = (MyObject)builder.BuildUp(locator, typeof(MyObject), null, null);
//createdType had the mapped type, MyOtherObject
Assert.IsInstanceOfType(typeof(MyOtherObject), createdType.Parameter);
We should take note of several items in the example above.
- The type indirection, if any, is indicated in the 3rd parameter. This means we are providing the mapping ourselves. If ObjectBuilder is configured for a TypeMappingPolicy instance on the Object type, it doesn't matter. It will create an instance of the createType parameter specified in the constructor. If you omit the createType, ObjectBuilder will instead attempt to create the parameterType value instead.
- NotePresentBehavior enum tells ObjectBuilder what to do when the objectName parameter is not found. You can choose to create a new instance, supply a null, or throw an exception. In general you would use the CreateNew value unless you are in manually managing singleton instances.
- SearchMode enum tells ObjectBuilder how far an wide it should search when locating a named object (see above). Normally you will search the local Locator instance unless you have a chain of containers (another article).
So you are probably looking at this list and thinking to yourself, "Why would I want to manage all this by myself instead of using the CreationParameter"? Well I can think of the primary reason is using ObjectBuilder as a platform to bootstrap your own DI component. When you use a container based approach to programming and configuration you need exactly this level of control, component by component.
Rule #2: ObjectBuilder is not a DI library, it is a foundation to create a DI library.
Another handy use case that people run into quite a bit is when you need to ask for a type that provides some bit of tastey functionality but your implemenation is flexible enough to do work on layers or chains of the same type (e.g. a Decorated Command Orientated Interface). Well if you relied on type mapping alone you would only create an infinite chain of the same type until your stack exploded. You see, each constructor depends on the same interface, lets say Foo, including the concrete type at the top, ObjectBuilder investigates the constructor, sees it needs type Foo and so uses the type mapping to resolve to the same type that starts the circular reference. So in this case you would use the type mapping for the top level object and explicitly set the inner instances yourself to concrete types via the DependencyParameter type. Neato, huh?
This little guy here is all about using the capabilities of the Locator class to fulfill constructor parameter resolution. Basically it uses the supplied key directly on the ILocator.Get(Object) method. Whatever you get from the locator for that call, you'll get when you use it as a IParameter call.
//Assumes variable constructor is appropriate ConstructorInfo instance
//Assumes variable locator is the loaded locator instance
Object key = "some value";
IParameter parameter = new LookupParameter(key);
ConstructorPolicy policy = new ConstructorPolicy(constructor, parameter);
builder.Policies.Set<ICreationPolicy>(policy, typeof (MyObject), null);
MyObject myObject = (MyObject)builder.BuildUp(locator, typeof(MyObject), null, null);
Object o1 = myObject.Parameter;
Object o2 = locator.Get(key);
Assert.AreSame(o1, o2);
This parameter type is actually an implementation of the decorator pattern applied to IParameter. If the inner IParameter instance returns a type that supports the ICloneable interface, it is invoked returning the clone; otherwise the original parameter value.
Rule #3: Decoration is a very powerful technique that can provide all sorts of cross-cutting functionality in your DI library. In example: every type needs to be wrapped in a logging proxy for trace analysis. I expect lots from the community to provide all sorts of interaction with aspect weavers and the Enterprise Library Policy Application block in the future.
This parameter type is used to provide specific instances or values to a constructor. Normally this is used to provide a literal as a constructor parameter during build up (i.e. a string). It comes in two varieties, a version typed as Object and one that is implemented as a generic.
ValueParameter<String> parameter = new ValueParameter<String>("My value");
This issue will be the topic of a subsequent article.
So there you have it folks! Enjoy your cherry-picked constructors!
Read part 5
|
|
Jimmy Zimms is currently feeling old and contemplating not having any more birthdays
|
|