简体   繁体   English

autofac:多次注册具有不同依赖项的同一个类

[英]autofac: registering the same class more than once with different dependencies

Given the following heirarchy: 鉴于以下层次结构:
I need to resolve one instance of ProducerRepository per registered IParser dependency when an IEnumerable<ISourceRepository<IDatum>> is resolved. 解决IEnumerable<ISourceRepository<IDatum>>时,我需要为每个注册的IParser依赖关系解析一个ProducerRepository实例。

public class ProducerRepository : ISourceRepository<Data>
{
    public ProducerRepository(IRepository repository)
    {}
}

public interface ISourceRepository<out T> where T : class, IDatum
{}

public class Repository : IRepository
{
    public Repository(IParser parser)
    {}
}

public class ParserTypeOne : IParser {}
public class ParserTypeTwo : IParser {}

I need to register ProducerRepository as follows. 我需要按以下方式注册ProducerRepository

builder.RegisterType<ProducerRepository>()           
.WithParserTypeOne() // how do I specify this as a particular dependency
.As<ISourceRepository<IDatum>();

builder.RegisterType<ProducerRepository>()          
.WithParserTypeTwo() // another type of parser.
.As<ISourceRepository<IDatum>>();

// OR: better still if at all possible...
builder.RegisterType<ProducerRepository>() // one of these for each underlying dependency
.WithEachPossibleTypeOfParser() // one for each dependency
.As<ISourceRepository<IDatum>>();

I need to know how to register the class twice, once with each type of dependency, so that when an IEnumerable<ISourceRepository<IDatum>> is resolved I get a separate instance of ProducerRepository for each different implementation of the parser. 我需要知道如何两次注册该类,每种类型的依赖项一次,以便在解析IEnumerable<ISourceRepository<IDatum>> ,为解析器的每个不同实现提供一个ProducerRepository的单独实例。

Better still, it would be nice to be able to register a separate ProducerRepository for each instance of the parser found in the assembly scan, and add it. 更好的是,能够为在程序集扫描中找到的解析器的每个实例注册一个单独的ProducerRepository并添加它,将是一个很好的选择。

I was hoping not to have to resort to changing the implementation to use Keyed or Meta registrations as that locks in each dependency and changes the implementation. 我希望不必诉诸于更改实现以使用KeyedMeta注册,因为这会锁定每个依赖项并更改实现。 If that is the only way of doing it, I will accept the answer. 如果那是唯一的方法,我将接受答案。 I am hoping for a generic way to register all the possible implementations . 我希望找到一种通用的方法来注册所有可能的实现

If we reduce the problem, you want that : 如果我们减少问题,您希望这样做:

public class Pouet
{
    public Pouet(Foo foo) { }
}
public class Foo
{
    public Foo(IBar bar) { }
}
public interface IBar { }
public class Bar1 : IBar { }
public class Bar2 : IBar { }

and

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Pouet>().AsSelf();
    builder.RegisterType<Foo>().AsSelf();
    builder.RegisterType<Bar1>().As<IBar>();
    builder.RegisterType<Bar2>().As<IBar>();
    IContainer container = builder.Build();

    IEnumerable<Pouet> pouets = container.Resolve<IEnumerable<Pouet>>();

    Console.WriteLine(pouets.Count()); // => must return 2 here !

Basically, to return more than 1 Pouet using container.Resolve<IEnumerable<Pouet>>() container must have more than 1 registration of Pouet same thing for Foo . 基本上,要使用container.Resolve<IEnumerable<Pouet>>()返回多个Pouet container.Resolve<IEnumerable<Pouet>>() container必须为Foo拥有1个以上Pouet相同的注册。 In order to do that, you can make this ugly thing : 为了做到这一点,你可以做这个丑陋的事情:

    // don't do that
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Pouet>().Named<Pouet>("initial");
    builder.RegisterType<Foo>().Named<Foo>("initial");
    builder.Register(c => c.ResolveNamed<Pouet>("initial", TypedParameter.From<Foo>(c.Resolve<IEnumerable<Meta<Foo>>>().Where(m => (Int32)m.Metadata["Bar"] == 1).Select(m => m.Value).First()))).As<Pouet>().WithMetadata("Bar", 1);
    builder.Register(c => c.ResolveNamed<Pouet>("initial", TypedParameter.From<Foo>(c.Resolve<IEnumerable<Meta<Foo>>>().Where(m => (Int32)m.Metadata["Bar"] == 1).Select(m => m.Value).First()))).As<Pouet>().WithMetadata("Bar", 2);
    builder.Register(c => c.ResolveNamed<Foo>("initial",TypedParameter.From<IBar>(c.Resolve<IEnumerable<Meta<IBar>>>().Where(m => (Int32)m.Metadata["Bar"] == 1).Select(m => m.Value).First()))).As<Foo>().WithMetadata("Bar", 1);
    builder.Register(c => c.ResolveNamed<Foo>("initial",TypedParameter.From<IBar>(c.Resolve<IEnumerable<Meta<IBar>>>().Where(m => (Int32)m.Metadata["Bar"] == 2).Select(m => m.Value).First()))).As<Foo>().WithMetadata("Bar", 2);
    builder.RegisterType<Bar1>().As<IBar>().WithMetadata("Bar", 1);
    builder.RegisterType<Bar2>().As<IBar>().WithMetadata("Bar", 2);

    IContainer container = builder.Build();

    IEnumerable<Pouet> pouets = container.Resolve<IEnumerable<Pouet>>();

    Console.WriteLine(pouets.Count()); // => 2 !

If you add a new intermediate dependency or a new IBar , you will have to write a lot of code. 如果添加新的中间依赖项或新的IBar ,则必须编写大量代码。 Using a custom IRegistrationSource , it should be possible to automate these uglies registrations but it will be quite complex and not really efficient. 使用自定义的IRegistrationSource ,应该可以自动执行这些丑陋的注册,但是它将非常复杂并且效率不高。

Another solution would be to play with lifetimeScope : 另一个解决方案是使用lifetimeScope:

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Pouet>().AsSelf();
    builder.RegisterType<Foo>().AsSelf();
    builder.RegisterType<Bar1>().As<IBar>();
    builder.RegisterType<Bar2>().As<IBar>();

    IContainer container = builder.Build();

    using (ILifetimeScope workScope = container.BeginLifetimeScope())
    {
        List<Pouet> pouets = new List<Pouet>();

        foreach (IComponentRegistration registration in container.ComponentRegistry.RegistrationsFor(new TypedService(typeof(IBar))))
        {
            using (ILifetimeScope scope = container.BeginLifetimeScope(cb => cb.RegisterComponent(registration)))
            {
                Pouet pouet = scope.Resolve<Pouet>();
                workScope.Disposer.AddInstanceForDisposal(scope);
                pouets.Add(pouet);
            }
        }

        Console.WriteLine(pouets.Count);
    }

By the way this code could be used inside an IRegistrationSource so you should be able to resolve IEnumerable<Pouet>() on your container. 通过这种方式可以在IRegistrationSource内部使用此代码,因此您应该能够在容器上解析IEnumerable<Pouet>()

If possible, I would recommend to refactor your code and make it simpler, playing with instance coming from another ILifetimeScope could be problematic and I'm not really confident about disposal of these instances : what happens if a type is registered as InstancePerLifetimeScope (I have not tried) ? 如果可能的话,我建议您重构代码并使之更简单,使用来自另一个ILifetimeScope实例可能会出现问题,并且我对处置这些实例并不十分有信心:如果将类型注册为InstancePerLifetimeScope会发生什么情况(我有没有尝试过)? etc. 等等

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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