Windsor: for all tastes

March 7th, 2008

So, in donjon code base we have service like the following:

public class AppStarter
{
  public AppStarter(IModuleInstaller[] installers) 
  {
     ...

By default, Castle.MicroKernel will expect you to define what should be included in the array. This can be done through the external configuration or through the code, using the cool fluent interface Craig has developed.

In my case, though, I always want all module installers registered to be available to my service. How to accomplish that?

The first guess is using a custom ITypeConverter implementation. However, those are only used by the container to convert a representation into something that can fulfill a dependency. This is definitely not my case.

Second guess is using a custom dependency resolver. “What? You mean I need to write a new dependency resolver for the whole damn thing?” No, not really necessary. The default dependency resolver allow you to add more sub resolver to handle your special situations. So all I had to do was:

class ArrayResolver : ISubDependencyResolver
{
	private readonly IKernel kernel;

	public ArrayResolver(IKernel kernel)
	{
		this.kernel = kernel;
	}

	public object Resolve(CreationContext context, ISubDependencyResolver parentResolver, 
                              ComponentModel model,
                              DependencyModel dependency)
	{
		return kernel.ResolveAll(dependency.TargetType.GetElementType(), null);
	}

	public bool CanResolve(CreationContext context, ISubDependencyResolver parentResolver, 
                              ComponentModel model,
                              DependencyModel dependency)
	{
		return dependency.TargetType != null && 
			dependency.TargetType.IsArray && 
			dependency.TargetType.GetElementType().IsInterface;
	}
}

And then, when you instantiate your container, you have to register the sub dependency resolver:

Kernel.Resolver.AddSubResolver(new ArrayResolver(Kernel));

A few lines of code, and you just got Windsor doing exactly what you want.

13 Responses to “Windsor: for all tastes”

Jeff Brown Says:

Excellent!
I’ve been meaning to do something like this for ages.

Having to resolve arrays of dependencies is one of the few places I end up violating container ignorance in my components because I end up having to depend on the kernel so I can do the resolve.

Any chance we could make this behavior standard? It would be really useful with components that function as mediators or facades over other services.

hammett Says:

The problem of making this behavior standard is the possible backward compatibility ghost. If you propose this on the devel list, we could certainly run a vote and decide it.

Jeff Brown Says:

It could be encapsulated as a standard facility, like Startable.

hammett Says:

That’s true.

Tuna Toksoz Says:

Suppose that I need an array of components which may change during the lifetime of the application. I mean, think of a plugin stuff. User may activate a plugin, or deactivate a plugin. So one time, windsor needs to bring this plugin, one time it doesn’t need it because it is inactive.
The obvious solution is to have IKernel as a dependency, and make a call to ResolveAll all the time. But this disturbs me a little as it violates container ignorance. I wonder if it is a good practice to have my own IDependencyResolver and make the implementation so that calls to my deependency resolver will end up with calls to the container?

hammett Says:

Yes, IDependencyResolver is the way to go – if the dependency cannot be reached by the standard ways though.

Steven Radack Says:

I tried this approach, but it appears that this specific implementation of this technique will ignore service overrides that are specified when configuring the kernel. Is it possible to have another version that will abide by overrides made?

I looked into modifying this implementation to return false in the CanResolve() method when a service override is made. One of the problems I had was that MicroKernel treats service overrides of array parameters as parameter dependencies instead of service override dependencies. My hope was that I could simply add (dependency.DependencyType != DependencyType.ServiceOverride) into the if condition but that doesn’t work. Any ideas?

Steven Radack Says:

Nevermind. I answered my own question. I added (&& model.Parameters.Count == 0) as an additional condition of the CanResolve() predicate. That seemed to do the trick.

DI Framework Challenges, 2: Lists » Blog Archive » I Think It’s Interesting Says:

[...] Thanks to Victor Kornov, I have found a this post by Castle developer, which describes the same solution. I have added a proposition to the [...]

Stefan Says:

I am having issues this does not working using the latest windsor from trunk? It does not call the resolver at all, my init looks like so:

// Init and setup container.
this.container = new WindsorContainer(new XmlInterpreter(new ConfigResource(“registry”)));
this.container.Kernel.Resolver.AddSubResolver(new ArrayResolver(this.container.Kernel));

public T Resolve() where T : class {
return container.Resolve();
}

Then I have a class like so:

public class Test2 : ITest2 {
public IEnumerable Loggers { get; set; }

public Test2(ILogger[] loggers) {
this.Loggers = loggers;
}
}

running var test = Factory.Instance.Resolve();

Gives me an error: “Can’t create component ‘test2′ as it has dependencies to be satisfied.
test2 is waiting for the following dependencies:”

Am I missing something obvious here?

Thanks

Johannes Says:

Stefan: It seems you need to add the resolver before you configure the container. Something like:

// Init and setup container
this.container = new WindsorContainer();

this.container.Kernel.Resolver.AddSubResolver(new ArrayResolver(this.container.Kernel));

this.container.Install ( Windsor.Installer.Configuration.FromXml(ConfigResource(”registry”)));

This worked for me anyway.

Regards
Johannes

Paul Stovell says… » Presentation: QMSDNUG Smart Client Patterns Says:

[...] also used an Array Resolver extension for resolving constructors that take [...]

Neil Barnwell Says:

You said “This can be done through the external configuration or through the code, using the cool fluent interface Craig has developed.”. Do you know where I can find an example or docs for this?

Struggling a bit, I’ll admit… :)

Leave a Reply