1

I have a MVC application, and i am using Autofac to resolve dependencies.

I have a situation where i have to create 2 containers and runtime should decide which container to use based on a condition.

The condition is if the controller Home is called, i need to use container1, or else i have to use container2.

Application_Start is the place where I register the container.

I am not sure how to make this happen at runtime. Any help is highly appreciated.

Thanks

Steven
  • 166,672
  • 24
  • 332
  • 435
user2388339
  • 27
  • 1
  • 4
  • How many registrations differ between the containers? It may make more sense to have a delegate factory, or some other way to alter an instantiation at runtime – ESG Nov 04 '14 at 01:25
  • some registrations vary, will be great if u could help me out with some code samples of doing tat. – user2388339 Nov 04 '14 at 01:40
  • I suggest looking at the [Selection of an implementer](https://github.com/autofac/Autofac/wiki/Component-Creation) section of the Autofac doc. You should also better explain what you are trying to do in the first place, rather than ask the details of a specific implementation. – ESG Nov 04 '14 at 01:55
  • Ok.. This is what am trying to do. My application have two kind of operation.. For the first operation, i have my dependencies been setup and my container has got all the registrations setup, and now for the second implementation, am creating another container as some of the types have different registrations. So I have to know how to use Autofac with multiple containers and how do i setresolvers for those. – user2388339 Nov 04 '14 at 02:06
  • Please update your question and describe in detail what you're trying to achieve and why, because having multiple container instances a is a highly unlikely scenario which which often causes as much trouble as it solves. If you describe in enough detail your design, we might be able to comment on that and show how you can solve your problems with one single container; or how to use multiple containers. – Steven Nov 04 '14 at 09:56

1 Answers1

2

One reason for letting controllers resolve from different containers is if your application consists of multiple isolated modules. In that case you might not want the modules to influence each other and having a container per module makes sense. In almost all other situations however, it doesn't make sense to have multiple container instances.

So if you need this, you need to build your own custom IControllerFactory that can switch containers based on the controller type. For instance, something like this:

internal sealed class MultipleContainerControllerFactory : IControllerFactory
{
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var factory = this.GetFactory(requestContext);
        var controller = factory.CreateController(requestContext, controllerName);

        // By storing the factory in the request items for this controller, 
        // we allow it to be easily retrieved
        // during ReleaseController and delegate releasing to the correct controller.
        HttpContext.Current.Items["ContrFct_" + controller.GetType().FullName] = factory;

        return controller;
    }

    public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext,
        string controllerName)
    {
        var factory = this.GetFactory(requestContext);
        return factory.GetControllerSessionBehavior(requestContext, controllerName);
    }

    public void ReleaseController(IController controller)
    {
        var controllerFactory = (IControllerFactory)HttpContext.Current.Items["ContrFct_" +
            controller.GetType().FullName];

        controllerFactory.ReleaseController(controller);
    }

    private IControllerFactory GetFactory(RequestContext context)
    {
        // return the module specific factory based on the requestcontext
    }
}

Besides this, you will need to have a special AutofacControllerFactory per container. That could look something like this:

public sealed class AutofacControllerFactory : DefaultControllerFactory
{
    public readonly IContainer Container;
    private readonly string moduleName;

    public AutofacControllerFactory(IContainer container, string moduleName)
    {
        this.Container = container;
        this.moduleName = moduleName;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            // The base method throws an expressive 404 HTTP error.
            base.GetControllerInstance(requestContext, controllerType);
        }

        // We need to start a new lifetime scope when resolving a controller.
        // NOTE: We can apply MatchingScopeLifetimeTags.RequestLifetimeScopeTag to the BeginLifetimeScope
        // method and in this case we can use .InstancePerRequest(), but in that case it becomes impossible to
        // verify the DI configuration in an integration test.
        ILifetimeScope lifetimeScope = this.Container.BeginLifetimeScope();

        // We need to store this lifetime scope during the request to allow to retrieve it when the controller
        // is released and to allow to dispose the scope. Memory leaks will be ensured if we don't do this.
        HttpContext.Current.Items[controllerType.FullName + "_lifetimeScope"] = lifetimeScope;

        // This call will throw an exception when we start making registrations with .InstancePerRequest, 
        // because the WebRequest functionality of Autofac is tied to the AutofacDependencyResolver, which we
        // don't use here. We can't use the AutofacDependencyResolver here, since it stores the created lifetime 
        // scope in the HttpContext.Items, but it uses a fixed key, which means that if we resolve multiple
        // controllers for different application modules, they will all reuse the same lifetime scope, while
        // this scope originates from a single container.
        try
        {
            return (IController)lifetimeScope.Resolve(controllerType);
        }
        catch (Exception ex)
        {
            lifetimeScope.Dispose();

            throw new InvalidOperationException("The container of module '" + this.moduleName +
                "' failed to resolve controller " + controllerType.FullName + ". " + ex.Message, ex);
        }
    }

    [DebuggerStepThrough]
    public override void ReleaseController(IController controller)
    {
        try
        {
            base.ReleaseController(controller);
        }
        finally
        {
            var scope = (ILifetimeScope)HttpContext.Current
                .Items[controller.GetType().FullName + "_lifetimeScope"];

            scope.Dispose();
        }
    }
}
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Steven
  • 166,672
  • 24
  • 332
  • 435