autofac:使用不同的依赖项多次注册相同的 class

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

鉴于以下层次结构:
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 {}

我需要注册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>>();

我需要知道如何注册 class 两次,每种依赖类型注册一次,这样当 IEnumerable<ISourceRepository<IDatum>> 被解析时,我得到为解析器的每个不同实现提供一个单独的 ProducerRepository 实例。

更好的是,如果能够为在程序集扫描中找到的每个解析器实例注册一个单独的 ProducerRepository 并添加它,那就太好了。

我希望不必诉诸更改实现以使用 KeyedMeta 注册,因为这会锁定每个依赖项并更改实现。如果那是唯一的方法,我会接受答案。 我希望有一种通用的方法来注册所有可能的实现

如果我们减少问题,您想要的是:

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 { }

    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 !

基本上,return 超过 1 Pouet 使用 container.Resolve<IEnumerable<Pouet>>() container 必须有超过 1 个注册 Pouet 同样的事情 Foo.为了做到这一点,你可以做这个丑陋的东西:

    // 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 !

如果你添加一个新的中间依赖或者一个新的IBar,你将不得不写很多代码。使用自定义 IRegistrationSource,应该可以自动执行这些丑陋的注册,但它会非常复杂且效率不高。

另一个解决方案是使用 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);
    }

顺便说一句,此代码可以在 IRegistrationSource 中使用,因此您应该能够在容器上解析 IEnumerable<Pouet>()

如果可能,我会建议重构您的代码并使其更简单,使用来自另一个 ILifetimeScope 的实例可能会出现问题,而且我对这些实例的处理不是很有信心:如果会发生什么一个类型注册为 InstancePerLifetimeScope (我没试过)?等等