将两个Autofac容器合并为一个

3
有没有一种方法可以将两个自动容器合并成一个?我的意思是,新容器中将包含来自两个容器的所有注册信息。或者,可以升级其中一个容器以包含另一个容器的信息。
例如:
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)

任何想法?
4个回答

4

您可以使用自定义的 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));

请注意,使用这种解决方案时需要小心,因为在使用复杂作用域场景时可能会引入问题。

1
需要注意的是,Update()在较新版本的Autofac中已被标记为过时,不应使用。请参见https://github.com/autofac/Autofac/issues/811#issuecomment-270246744以了解更多信息。
一些解决此问题的建议包括:
  • 传递ContainerBuilder而不是IContainer
  • 将注册添加到子作用域
  • 使用Lambda表达式
  • 使用模块
  • 使用条件注册(在4.4.0中新增)
基本上...尽量不要出现2个容器。

1
是的,这是可能的。
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
@danfromisrael 我会做完全相同的事情。update 方法被重载,可以接受 IComponentRegistry 或者 IContainer 作为参数。 - Stephen Ross
@Stephan Ross,感谢您的回答。我有两个准备好的IContainers需要合并成一个。已编辑问题并添加了示例。 - danfromisrael
@danfromisrael 好的,这有点不同。我可以问一下你是如何陷入拥有两个不同容器的情况的吗?你可以猜到这不是你想要的结果。如果这是因为你初始化了两个单独的实例并且想要合并它们,我认为最好研究一下 Autofac 的“模块”,因为它们允许可分离性,但在构建时也会合并为一个容器。如果不是这种情况,我认为你需要审视你的设计;在这里提供更多细节会很有帮助。 - Stephen Ross

0

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接