Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge 2 Autofac containers into one

is there a way i can merge 2 autoface container into 1? I mean that all registrations from both containers will be included in the new one Alternatively it could be upgrade one of them with the other

For example:

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

I've tried iterating over "container.ComponentRegistry.Registrations" and by using containerBuilder registering components and upgrading the second container but for some reason there were some conflicts

    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)

Any Ideas ?

like image 973
danfromisrael Avatar asked Oct 18 '25 15:10

danfromisrael


2 Answers

You can use a custom IRegistrationSource. This interface contains a RegistrationsFor method that return a list of IComponentRegistration for a specific IService

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

You can use it like this :

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

But be careful with this solution it may introduce issue when you use complex scope scenario.

like image 88
Cyril Durand Avatar answered Oct 21 '25 07:10

Cyril Durand


Yes it is possible.

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

var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.

newContainterBuilder.Update(existingContainer);

The update method essentially merges the contents of the existingContainer into the newContainerBuilder.

EDIT - If using an IContainer

If you have already built your ContainerBuilder into an IContainer the Update method of the ContainerBuilder is able to accept an IContainer as input. But in this case the already built container will now contain all the registrations and the new ContainerBuilder can go out of scope.

var builtContainer = existingContainer.Build();

var newContainerBuilder = new ContainerBuilder();
// Add registrations to new container.

newContainterBuilder.Update(builtContainer);
like image 23
Stephen Ross Avatar answered Oct 21 '25 07:10

Stephen Ross