16

是否可以根据其他组件的状态有条件地注册组件?就像是:

ContainerBuilder.RegisterConditionally<T>(
  Func<IComponentContext, bool>, 
  Func<IComponentContext, T>);

我发现在 autofac 的 V2 之前,可以使用“ Register().OnlyIf()”结构,这看起来就像我正在寻找的结构。我希望这样的功能有条件地覆盖默认注册。

class CommonRegistrations
{
  public virtual void Register(ContainderBuilder builder)
  {
    builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
    builder.RegisterType<DefaultFoo>().As<IFoo>();
  }
}

class SpecificRegistrations : CommonRegistrations
{
  public virtual void Register(ContainerBuilder builder)
  {
    base.Register(builder);
    builder.ConditionalyRegister(
      ctx => ctx.Resolve<ISettings>().ReallyUseSpecificFoo, 
      ctx => new SpecificFoo()).As<IFoo>();
  }
}

...

var builder = new ContainerBuilder();
var registrations = new SpecificRegistrations();
registrations.Register(builder);
var container = builder.Build();
IFoo foo = container.Resolve<IFoo>();

foo 将根据ISettings.ReallyUseSpecificFoo实例DefaultFoo或实例SpecificFoo

谢谢你。

4

1 回答 1

24

无法根据容器内容在容器级别执行条件注册。问题是您需要解决容器中的某些内容以确定在容器中注册的内容,这在技术上可能会影响您是否首先要注册该事物。鸡/蛋循环依赖问题。

但是,您可以有条件地将事物注册到嵌套的生命周期范围内。大多数集成点(如 ASP.NET)解析嵌套的生命周期范围(如 HTTP-request-length 生命周期范围)。您可以即时将事物注册到嵌套的生命周期范围内,这可能会解决您的问题。

var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();

var settings = container.Resolve<ISettings>();
using(var scope =
  container.BeginLifetimeScope(b => {
    if(settings.ReallyUseSpecificFoo)
    {
      b.RegisterType<SpecificFoo>().As<IFoo>();
    }
  })
{
  // Resolve things from the nested lifetime scope - it will
  // use the overrides. This will get the SpecificFoo if the
  // configuration setting is true.
  var foo = scope.Resolve<IFoo>();
}

您可以选择的另一个选择是将注册设置为 lambda。它可能会使注册本身更加复杂,但这是您可以考虑的一个选项。

var builder = new ContainerBuilder();
builder.Register(ctx => {
    var settings = ctx.Resolve<ISettings>();
    if(settings.ReallyUseSpecificFoo)
    {
      return new SpecificFoo();
    }
    return new DefaultFoo();
  }).As<IFoo>();

如果手动构建没有吸引力,您也可以通过 Autofac 传递它。

var builder = new ContainerBuilder();
// Register the IFoo types - but NOT "As<IFoo>"
builder.RegisterType<DefaultFoo>();
builder.RegisterType<SpecificFoo>();
// In the lambda use Resolve<T> to get the instances.
builder.Register(ctx => {
    var settings = ctx.Resolve<ISettings>();
    if(settings.ReallyUseSpecificFoo)
    {
      return ctx.Resolve<SpecificFoo>();
    }
    return ctx.Resolve<DefaultFoo>();
  }).As<IFoo>();

另一种选择是在构建后更新现有容器。在这种情况下,您可以通过实际构建容器、使用它并在事后更改注册来避免鸡/蛋场景。

var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();

var settings = container.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
  var updater = new ContainerBuilder();
  updater.RegisterType<SpecificFoo>().As<IFoo>();
  updater.Update(container);
}

最后,您可能会考虑 XML 配置。鉴于注册依赖于某种配置设置,您可以考虑使用Autofac 的 XML 配置支持。这样,您无需尝试从未构建的容器中解析某些内容以有条件地注册其他内容,而只需使用 XML 配置指定要注册的正确内容,并在第一时间注册正确的内容。

于 2013-09-24T20:32:26.457 回答