【问题标题】:Merge 2 Autofac containers into one将 2 个 Autofac 容器合并为一个
【发布时间】:2015-10-19 09:01:38
【问题描述】:

有没有办法可以将 2 个 autoface 容器合并为 1 个? 我的意思是两个容器的所有注册都将包含在新容器中 或者,可以将其中一个升级为另一个

例如:

public IContainer Merge(IContainer container1, IContainer container2)
{
     return ???;
}

我尝试过迭代“container.ComponentRegistry.Registrations”并使用 containerBuilder 注册组件并升级第二个容器,但由于某种原因出现了一些冲突

    Autofac.Core.DependencyResolutionException : An exception was thrown while executing a resolve operation. See the InnerException for details. ---> The provided instance has already been used in an activation request. Did you combine a provided instance with non-root/single-instance lifetime/sharing? (See inner exception for details.)
  ----> System.InvalidOperationException : The provided instance has already been used in an activation request. Did you combine a provided instance with non-root/single-instance lifetime/sharing?
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Container.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, ref Object instance)
   at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context)
   at Bootstrap.Bootstrapper.CreatePersistentTimerAsyncExecuter(IContainer container)
   at Bootstrap.Bootstrapper.Monitor(IContainer container)
   at Bootstrap.Bootstrapper.Boot(Assembly[] assemblies)
   at Soluto.Telemetry.Processing.Core.IntegrationTests.TelemetryProcessingBootstrapperTests.Boot_With2TelemetryHandlersInAssembly_ResolvesSubscriberWithItsHandlers() in TelemetryProcessingBootstrapperTests.cs: line 88
--InvalidOperationException
   at Autofac.Core.Activators.ProvidedInstance.ProvidedInstanceActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.<Execute>b__0()
   at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)

有什么想法吗?

【问题讨论】:

    标签: c# inversion-of-control autofac


    【解决方案1】:

    需要注意的是,Update() 在 Autofac 的较新版本中被标记为已过时,不应使用。有关更多说明,请参阅https://github.com/autofac/Autofac/issues/811#issuecomment-270246744

    列出的一些解决此问题的建议是:

    • 传递 ContainerBuilder 而不是 IContainer
    • 向子范围添加注册
    • 使用 Lambdas
    • 使用模块
    • 使用条件注册(4.4.0 中的新功能)

    基本上.. 如果可能的话,不要以 2 个容器结束。

    【讨论】:

      【解决方案2】:

      您可以使用自定义IRegistrationSource。此接口包含一个 RegistrationsFor 方法,该方法返回特定 IServiceIComponentRegistration 列表

      public class CrossContainerRegistrationSource : IRegistrationSource
      {
          public CrossContainerRegistrationSource(IComponentContext componentContext)
          {
              this._componentContext = componentContext;
          }
      
          private readonly IComponentContext _componentContext;
      
          public Boolean IsAdapterForIndividualComponents
          {
              get
              {
                  return false;
              }
          }
      
          public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, 
              Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
          {
              return this._componentContext.ComponentRegistry.RegistrationsFor(service);
          }
      }
      

      你可以这样使用它:

      container2.ComponentRegistry.AddRegistrationSource(new CrossContainerRegistrationSource(container1));
      

      但是要小心这个解决方案,当您使用复杂的范围场景时,它可能会引入问题。

      【讨论】:

        【解决方案3】:

        是的,这是可能的。

        var existingContainer = new ContainerBuilder();
        // Add registrations to existing container.
        
        var newContainerBuilder = new ContainerBuilder();
        // Add registrations to new container.
        
        newContainterBuilder.Update(existingContainer);
        

        update 方法本质上是将existingContainer 的内容合并到newContainerBuilder 中。

        编辑 - 如果使用 IContainer

        如果您已经将ContainerBuilder 构建到IContainer 中,则ContainerBuilderUpdate 方法能够接受IContainer 作为输入。但在这种情况下,已经构建的容器现在将包含所有注册,并且新的 ContainerBuilder 可能会超出范围。

        var builtContainer = existingContainer.Build();
        
        var newContainerBuilder = new ContainerBuilder();
        // Add registrations to new container.
        
        newContainterBuilder.Update(builtContainer);
        

        【讨论】:

        • 在您的示例中,“existingContainer”是一个 ContainerBuilder。我拥有的是一个 IContainer 实例。在这种情况下你会怎么做?
        • @danfromisrael 我也会这样做。 update 方法被重载,因此它需要 IComponentRegistryIContainer
        • @Stephan Ross 感谢您的回答。我有 2 个准备好的 IContainers 我需要合并为 1 个。编辑了问题并添加了示例
        • @danfromisrael 好的,这有点不同。我能问一下您是如何陷入拥有 2 个不同容器的情况的吗?你可以猜到这不是你想去的地方。如果这是因为您已经初始化了两个单独的实例并想要合并它们,我认为查看 Autofac modules 可能会很好,因为它们允许可分离性,但也会在构建时合并到一个容器中。如果不是这种情况,我认为您处于需要查看设计的状态;更多细节在这里会有所帮助。
        【解决方案4】:

        谢谢大家的回答, 最终我根据自己的需要做了一些更具体的事情:

        public void Merge(this IContainer container1, IContainer container2)
        { 
            var newBuilder = new ContainerBuilder();
            newBuilder.RegisterInstance(container2.Resolve<ISomeService1>()).AsImplementedInterfaces();
            newBuilder.RegisterInstance(container2.Resolve<ISomeService2>()).AsImplementedInterfaces();
        
            newBuilder.Update(container1);
            return container1;
        }
        

        【讨论】:

          猜你喜欢
          • 2021-09-22
          • 2018-04-26
          • 1970-01-01
          • 1970-01-01
          • 2014-05-31
          • 2017-09-22
          • 1970-01-01
          • 1970-01-01
          • 2018-01-16
          相关资源
          最近更新 更多