将 2 个 Autofac 容器合并为一个
Merge 2 Autofac containers into one
有什么方法可以将 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)
有什么想法吗?
是的,这是可能的。
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
中,ContainerBuilder
的 Update
方法能够接受 IContainer
作为输入。但是在这种情况下,已经构建的容器现在将包含所有注册,并且新的 ContainerBuilder
可以超出范围。
var builtContainer = existingContainer.Build();
var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.
newContainterBuilder.Update(builtContainer);
您可以使用自定义 IRegistrationSource
。此接口包含一个 RegistrationsFor
方法,该方法 return 用于特定 IService
的 IComponentRegistration
列表
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));
但请注意此解决方案,当您使用复杂范围场景时,它可能会引入问题。
感谢大家的回答,
最终我做了一些更具体的事情来满足我的需要:
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;
}
需要注意的是,Update()
在较新版本的 Autofac 中被标记为过时,不应使用。有关更多说明,请参阅 https://github.com/autofac/Autofac/issues/811#issuecomment-270246744。
列出的一些解决此问题的建议是:
- 传递 ContainerBuilder 而不是 IContainer
- 向子范围添加注册
- 使用 Lambdas
- 使用模块
- 使用条件注册(4.4.0 中的新功能)
基本上..如果可能,不要以 2 个容器结束。
有什么方法可以将 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)
有什么想法吗?
是的,这是可能的。
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
中,ContainerBuilder
的 Update
方法能够接受 IContainer
作为输入。但是在这种情况下,已经构建的容器现在将包含所有注册,并且新的 ContainerBuilder
可以超出范围。
var builtContainer = existingContainer.Build();
var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.
newContainterBuilder.Update(builtContainer);
您可以使用自定义 IRegistrationSource
。此接口包含一个 RegistrationsFor
方法,该方法 return 用于特定 IService
IComponentRegistration
列表
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));
但请注意此解决方案,当您使用复杂范围场景时,它可能会引入问题。
感谢大家的回答, 最终我做了一些更具体的事情来满足我的需要:
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;
}
需要注意的是,Update()
在较新版本的 Autofac 中被标记为过时,不应使用。有关更多说明,请参阅 https://github.com/autofac/Autofac/issues/811#issuecomment-270246744。
列出的一些解决此问题的建议是:
- 传递 ContainerBuilder 而不是 IContainer
- 向子范围添加注册
- 使用 Lambdas
- 使用模块
- 使用条件注册(4.4.0 中的新功能)
基本上..如果可能,不要以 2 个容器结束。