conditional component registration in autofac

后端 未结 1 653
半阙折子戏
半阙折子戏 2021-02-13 03:23

Is it possible to register a component conditionally on an other component\'s state? Something like:

ContainerBuilder.RegisterConditionally(
  Func<         


        
相关标签:
1条回答
  • 2021-02-13 04:08

    There is no way to perform conditional registration at the container level based on container contents. The trouble is that you would need to resolve something in the container in order to determine what gets registered in the container, which then could technically affect whether you wanted to register the thing in the first place. Chicken/egg circular dependency problem.

    You can, however, register things conditionally into nested lifetime scopes. Most integration points (like ASP.NET) resolve out of nested lifetime scopes (like an HTTP-request-length lifetime scope). You can register things on the fly into nested lifetime scopes and that might solve your problem.

    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>();
    }
    

    Another option you have is to make the registration a lambda. It might make the registration itself more complex but it's an option you could consider.

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

    If manual construction there isn't appealing, you could pass it through Autofac, too.

    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>();
    

    Yet another option is to update an existing container after being built. In this case, you avoid the chicken/egg scenario by actually building the container, using it, and changing registrations after the fact.

    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);
    }
    

    Finally, you might consider XML configuration. Given the registration is dependent on some sort of configuration setting, you might consider using Autofac's XML configuration support. That way, instead of trying to resolve something out of an un-built container to conditionally register something else, you could just specify the right thing to register using the XML configuration and register the correct thing the first time.

    0 讨论(0)
提交回复
热议问题