autofac中的条件组件注册

时间:2013-09-24 17:28:28

标签: c# dependency-injection autofac

是否可以有条件地在其他组件的状态下注册组件?类似的东西:

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的实例。

谢谢。

1 个答案:

答案 0 :(得分:19)

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

但是,您可以有条件地将事物注册到嵌套的生命周期范围中。大多数集成点(如ASP.NET)都会解析出嵌套的生命周期范围(如HTTP请求长度的生命周期范围)。您可以动态地将事物注册到嵌套的生命周期范围中,这可能会解决您的问题。

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's XML configuration support。这样,您可以指定使用XML配置进行注册并在第一次注册正确的内容时,而不是尝试从未构建的容器中解析某些内容以有条件地注册其他内容。

相关问题