views:

319

answers:

2

I'm programmatically registering a group of services that all implement the same interface, IRule. I have another service that looks like this:

public class MyService {
    private IEnumerable<IRule> _rules;
    public MyService(IEnumerable<IRule> rules){
        _rules = rules;
    }
}

Hammett posted something that looked like what I wanted, http://hammett.castleproject.org/?p=257. I changed the signature to IRule[] and tried the ArrayResolver trick in the post but that didn't work for me(note, it didn't break anything either).

Anyone know how to programmatically register a component like the code I posted above?

A: 

I loaded up the source for Castle.MicroKernel, and noticed there is already an ArrayResolver and ListResolver(in the Castle.MicroKernel.Resolvers.SpecializedResolvers namespace). The code I copied(blindly) from Hammet's blog didn't work, most likely because the framework had changed since that was written.

Here's a sample project demonstrating how to do this: http://www.panteravb.com/downloads/WindsorCon.zip

I tried both the ArrayResolver and the ListResolver and they both worked without a hitch, it's pretty straightforward, so assuming this service class:

public class MyService
{
    private IEnumerable<IRule> _rules;
    public MyService(IList<IRule> rules)
    {
        _rules = rules;
    }
}

you can register this guy like this:

private IWindsorContainer _container;
private void InitializeIoc()
{
    _container = new WindsorContainer();
    _container.Kernel.Resolver.AddSubResolver(new ListResolver(_container.Kernel));
    _container.Register(Component.For<IRule>().ImplementedBy<Rule1>());
    _container.Register(Component.For<IRule>().ImplementedBy<Rule2>());
    _container.Register(Component.For<MyService>());
}
Chris Carter
+1  A: 

If you don't want to change the signature of MyService and keep using IEnumerable<IRule>, you can also create a custom ISubDependencyResolver. That's what we did:

public class EnumerableResolver : ISubDependencyResolver
{
    private readonly IKernel kernel;

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

    public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        Type targetType = dependency.TargetType;
        if (targetType == null)
        {
            throw new ArgumentException("TargetType property cannot be null", "dependency");
        }

        if (targetType.IsGenericType && (targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
        {
            Type service = targetType.GetGenericArguments()[0];
            return this.kernel.HasComponent(service);
        }
        return false;
    }

    public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        Type service = dependency.TargetType.GetGenericArguments()[0];
        Array array = this.kernel.ResolveAll(service, (IDictionary)null);
        return Activator.CreateInstance(typeof(List<>).MakeGenericType(new Type[] { service }), new object[] { array });
    }
}

It needs to be registered with the container like this:

container.Kernel.Resolver.AddSubResolver(new EnumerableResolver(this.Kernel));
Mark Seemann