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
并添加它,那就太好了。
我希望不必诉诸更改实现以使用 Keyed
或 Meta
注册,因为这会锁定每个依赖项并更改实现。如果那是唯一的方法,我会接受答案。 我希望有一种通用的方法来注册所有可能的实现。
如果我们减少问题,您想要的是:
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
(我没试过)?等等
鉴于以下层次结构:
当 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
并添加它,那就太好了。
我希望不必诉诸更改实现以使用 Keyed
或 Meta
注册,因为这会锁定每个依赖项并更改实现。如果那是唯一的方法,我会接受答案。 我希望有一种通用的方法来注册所有可能的实现。
如果我们减少问题,您想要的是:
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
(我没试过)?等等