Intermediate IoC...
implementation... first off you get a handle of the XML
configuration, registering components, using existing
facilities... pretty much getting away largely with cut 'n paste
coding.
Then you get to the intermediate level.. writing basic
facilities, tweaks to the component model, writing your own sub
dependency resolvers or component activator, having a go with
binsor configuration.
I think the third level is reserved for the Castle team alone...
;o)
This post is going to be straddling the beginner to intermediate
kinda level... which is generally all I ever reach with Castle's
IoC... it's not often you have to dig deeper day-to-day... though
it's always good to know there is a lot of untapped potential
there.
So.. for today, say you have a component, like the Base4Host, which has some explicit constructors:
public
Base4Host(string appName,
int port)
{
if
(string.IsNullOrEmpty(appName))
throw new ArgumentNullException("appName");
if (port
<= 1024)="">=>throw
new ArgumentOutOfRangeException("port",
"port should be greater then
1024");
_appName = appName;
_port = port;
}
public
Base4Host(string appName,
int port, string root)
: this(appName, port)
{
if
(string.IsNullOrEmpty(root))
throw new ArgumentNullException("root");
_root = root;
}
You can register it the container easy enough, and provide values
for them in XML configuration, but what if you want to do the
same programatically... generally your first stop would be to
examine the IWindsorContainer for a suitable overload...
alas it doesn't get us far, so we dig in to the underlying
IKernel itself... the kernel exposes some possible
candidates:
void
AddComponentWithExtendedProperties(String key, Type classType, IDictionary extendedProperties);
void
AddComponentWithExtendedProperties(String key, Type serviceType, Type classType, IDictionary extendedProperties);
So you experiment with them, but supplying the dictionary of
extended properties does nothing... hmm... time to file a bug
report? well no... extended properties having nothing to do with
satisfying parameter or property dependencies on your component -
not directly at least.
So why don't we just create the component ourselves.. and then
add it to the container?
Well you can, via the Kernel.AddComponentInstance method
but you're going to miss out on some things... for instance the
startable facility won't be "concerned" with your component, and
as such if it implements IStartable it won't get started
and stopped... Though I haven't confirmed this, I dont think the
container will bother to dispose of any IDisposable
components registered in this fashion either... the container
doesn't consider itself the owner of the component (and generally
this is what we want).
So we're going to have to get a little more intimate with the
container implementation ... so every time a component is
registered in the container a corresponding ComponentModel
is generated for the component, this basically keeps track of
the:
- Components dependencies
- Constructor candidates
- Parameters (sounds like us...)
- Name, implementation type and service type.
- Lifecycle, Lifestyle...
- And some other stuff you can discover for yourself.
the construction of the component model itself... but it's pretty
uncessary, we just want to tweak the end result... so we can use
an event handler on the Kernel -
ComponentModelCreated.
So here we have an implementation that solves our problems...
This is being implemented inside a facility, but you could do
this anywhere... wire it up in your custom container that's
derived from WindsorContainer maybe, obviously you want to
remove the if statement for checking the Implemenation type is
Base4Host though. :)
private const string AdditionalParametersKey =
"AdditionalParameters";
private void
Kernel_ComponentModelCreated(Castle.Core.ComponentModel model)
{
if
(model.Implementation == typeof(Base4Host))
{
if ((model.Configuration ==
null)
&&
model.ExtendedProperties.Contains(AdditionalParametersKey))
{
Dictionarystring, object> additionalParameters =
(Dictionarystring, object>)model.ExtendedProperties[AdditionalParametersKey];
foreach (string parameterName in additionalParameters.Keys)
{
model.Parameters.Add(parameterName,
Convert.ToString(additionalParameters[parameterName]));
}
}
}
}
protected override void Init()
{
Kernel.ComponentModelCreated +=
new ComponentModelDelegate(Kernel_ComponentModelCreated);
}
Now I can register my component by doing something like
this:
Dictionarystring, object> additionalParameters =
new Dictionarystring, object>();
additionalParameters.Add("appName", _applicationName);
additionalParameters.Add("port", _port);
additionalParameters.Add("root", _baseDirectory);
Hashtable properties =
new Hashtable();
properties.Add(AdditionalParametersKey,
additionalParameters);
Kernel.AddComponentWithProperties("base4.defaultHost", typeof(Base4Host), properties);
Here we're passing all our additional parameters as a
Dictionary
IDictionary of additional properties.... this isn't
the most elegant implementation, but this is all code internal to
a single facility so it's not really important to me... and it
gets the job done just fine.