danfromisrael
danfromisrael

Reputation: 3112

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 ?

Upvotes: 4

Views: 3043

Answers (4)

tesharp
tesharp

Reputation: 41

It should be noted that Update() is marked obsolete in newer versions of Autofac and should not be used. See https://github.com/autofac/Autofac/issues/811#issuecomment-270246744 for more clarification.

Some of the recommendations listed to get around this issue is:

  • Pass Around ContainerBuilder Instead of IContainer
  • Add Registrations to Child Scopes
  • Use Lambdas
  • Use Modules
  • Use Conditional Registrations (new in 4.4.0)

Basically.. don't end up with 2 containers if possible.

Upvotes: 1

danfromisrael
danfromisrael

Reputation: 3112

Thanks for all the answers guys, eventually i did something more specific for my needs:

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

Upvotes: 1

Stephen Ross
Stephen Ross

Reputation: 892

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

Upvotes: 1

Cyril Durand
Cyril Durand

Reputation: 16192

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.

Upvotes: 4

Related Questions