I have described how does the ObjectBuilder work in the previous post. The reason why I even started to investigate the internals of a dead project is because of the WCSF – another dead project.
Since OB is a framework for building DI, the WCSF has created its own simple DI with two ways to build up objects – either as singletons or new objects. The depending objects can be inserted either through constructor or through properties.
The WCSF has its own builder of objects – the WCSFBuilder
class derived from WCSFBuilderBase
. It should be noted that when you diff the WCSFBuilderBase
from the WCSF and BuilderBase
from OB, they are quite similar and there was no reason to reimplamenetcopy&edit the base builder class.
The gist of WCSF are four strategies:
Strategies.AddNew<TypeMappingStrategy>(WCSFBuilderStage.PreCreation);
Strategies.AddNew<SimplifiedSingletonStrategy>(WCSFBuilderStage.PreCreation);
Strategies.AddNew<BuildPlanStrategy>(WCSFBuilderStage.Creation);
Strategies.AddNew<BuilderAwareStrategy>(WCSFBuilderStage.PostInitialization);
Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
Policies.SetDefault<IBuildPlanPolicy>(new BuildPlanPolicy());
Policies.SetDefault<IPlanBuilderPolicy>(CreatePlanBuilder());
private static IPlanBuilderPolicy CreatePlanBuilder()
{
BuilderStrategyChain chain = new BuilderStrategyChain();
chain.Add(new CallConstructorStrategy());
chain.Add(new SetPropertiesStrategy());
chain.Add(new CallMethodsStrategy());
PolicyList policies = new PolicyList();
policies.SetDefault<IConstructorChooserPolicy>(new AttributeBasedConstructorChooser());
policies.SetDefault<IPropertyChooserPolicy>(new AttributeBasedPropertyChooser());
policies.SetDefault<IMethodChooserPolicy>(new AttributeBasedMethodChooser());
return new DynamicMethodPlanBuilderPolicy(chain, policies);
}
As you can see, the OB has four chained strategies that are chained.
TypeMappingStrategy
This is strategy that preprocess the buildup request before passing it to the rest of chain, it doesn’t actually build the object. Its task is to change requested type to the type we actually to build up, in most cases it maps interfaces to concrete classes, e.g. ITimeService to NetworkTimeProtocolService. The precise mapping is defined by ITypeMappingPolicy.
SimplifiedSingletonStrategy
This strategy is quite simple:
- If locator contains an instance of requested object with id&type -> Return instance
- Otherwise build up the object using rest of chain, insert it to locator and return it.
The startegy checks and respects ISingletonPolicy
of builder. If the policy says no to singletons, new instance is not injected into locator.
BuildPlanStrategy
This is a candidate for The Daily WTF. When I go through all the stuff and dependency, it seems to create a specialmethod using ILGenerator
just for creation. We actually have things like il.Emit(OpCodes.Ldarg_2);
. Generating assembler at runtime… In 2008.
This strategy uses DynamicMethodPlanBuilderPolicy
that basically for each type creates a dynamically created method (using ILGenerator
and opcodes) for building an object of the type. The strategy then calls the method to create the object and passes the created object to the next link of the chain.
The interesting part is DynamicMethodPlanBuilderPolicy.CreatePlan
– the method returns dynamic method “BuildUp_” + typeToBuild that will be executed on typeToBuild class.
The code of method is generated sequentially by the chain from CreatePlanBuilder
from code snippet above:
// Code used to create a method that will build up the typeToBuild in the DynamicMethodPlanBuilderPolicy used by BuildPlanStrategy
ILGenerator il = buildMethod.GetILGenerator();
// In this chain is the CallConstructorStrategy, SetPropertiesStrategy and CallMethodsStrategy
context.HeadOfChain.BuildUp(context, typeToBuild, il, idToBuild);
il.Emit(OpCodes.Ret);
The CallConstructorStrategy
check if existing object is null, if not, it builds up parameters of constructor and calls it.
- The
SetPropertiesStrategy
builds up and sets objects for all marked properites ([CreateNew]/[ServiceDependency]
). - The
CallMethodsStrategy
– It will call all methods of the object that have the[InjectionMethod]
attribute with build up parameters.
Aaaargh! Is there any reason to do this instead of three link of build starategy chain, where
- first link creates an instance using constructor and passes it to the second one
- second link builds up and assigns instances to the
[CreateNew]
/[ServiceDependency]
properties of the object - third calls all
[InjectionMethod]
methods of the existing object.
UPDATE: Somebody probably thought so too, because there are three unused strategies that do exactly that: ConstructorReflectionStrategy
, PropertyReflectionStrategy
and MethodReflectionStrategy
.
Once the method creates the existing object, is is passed to the last strategy:
BuilderAwareStrategy
Post initialization task, this strategy checks if passed existing object is an instance of IBuilderAware
and if it is, the OB will call OnBuiltUp
method of the existing object.
I think this strategy is used only in tests of WCSF, e.g. if WCSF did build up a WCSF UserControl
.