简体   繁体   中英

How to programmatically register a component that depends on a list of already registered components with Castle Windsor?

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?

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));

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>());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM