Autofac注册多个容器

Posted

技术标签:

【中文标题】Autofac注册多个容器【英文标题】:Autofac Registering Multiple Containers 【发布时间】:2014-11-04 01:17:53 【问题描述】:

我有一个 MVC 应用程序,我正在使用 Autofac 来解决依赖关系。

我有一种情况,我必须创建 2 个容器,运行时应根据条件决定使用哪个容器。

条件是如果调用了控制器Home,我需要使用container1,否则我必须使用container2。

Application_Start 是我注册容器的地方。

我不确定如何在运行时实现这一点。非常感谢任何帮助。

谢谢

【问题讨论】:

容器之间有多少注册不同?使用委托工厂或其他方式在运行时更改实例化可能更有意义 有些注册会有所不同,如果你能帮我提供一些代码示例,那就太好了。 我建议查看 Autofac 文档的 Selection of an implementer 部分。您还应该首先更好地解释您正在尝试做什么,而不是询问具体实现的细节。 好的..这就是我想要做的。我的应用程序有两种操作.. 对于第一个操作,我已经设置了我的依赖项,并且我的容器已经设置了所有注册,现在对于第二个实现,我正在创建另一个容器,因为某些类型具有不同的注册。所以我必须知道如何将 Autofac 与多个容器一起使用,以及如何为这些容器设置解析器。 请更新您的问题并详细描述您想要实现的目标以及原因,因为拥有多个容器实例是极不可能的情况,它通常会带来与解决问题一样多的麻烦。如果您对您的设计进行了足够详细的描述,我们或许可以对此发表评论,并展示如何使用一个容器解决您的问题;或如何使用多个容器。 【参考方案1】:

让控制器从不同容器中解析的一个原因是,如果您的应用程序包含多个独立的模块。在这种情况下,您可能不希望模块相互影响,并且每个模块都有一个容器是有意义的。然而,在几乎所有其他情况下,拥有多个容器实例是没有意义的。

所以如果你需要这个,你需要构建自己的自定义IControllerFactory,它可以根据控制器类型切换容器。例如,这样的事情:

internal sealed class MultiplContainerControllerFactory : 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
    

除此之外,您还需要为每个容器提供一个特殊的AutofacControllerFactory。这可能看起来像这样:

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

【讨论】:

以上是关于Autofac注册多个容器的主要内容,如果未能解决你的问题,请参考以下文章

Autofac依赖注入

IoC容器Autofac正篇之类型注册

Autofac系列注册

Autofac和nopcommerce中的Autofac, 还有反射

Autofac之自动装配

Autofac实现有条件的DI