autofac中的条件组件注册

Jan*_*vák 12 c# dependency-injection autofac

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

ContainerBuilder.RegisterConditionally<T>(
  Func<IComponentContext, bool>, 
  Func<IComponentContext, T>);
Run Code Online (Sandbox Code Playgroud)

我发现在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>();
Run Code Online (Sandbox Code Playgroud)

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

谢谢.

Tra*_*lig 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>();
}
Run Code Online (Sandbox Code Playgroud)

另一个选择是使注册成为lambda.它可能会使注册本身更复杂,但它是您可以考虑的选项.

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

如果手动构造没有吸引力,您也可以通过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>();
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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