首页 文章

如何在ASP.NET MVC控制器中访问Simple Injector容器?

提问于
浏览
1

我想在Simple Injector中尝试一下Hybrid Lifestyle . 我希望有一个可选的显式定义的异步范围,如果没有定义异步范围,则回退到Web请求范围 .

我打算在ASP.NET MVC控制器中使用它,通常我想解决最外层Web请求范围的实例,但在某些情况下,我想在控制器的操作中显式创建一个异步范围并缩短范围内的实例的生活方式 .

要在控制器的操作中显式创建Async Scope,我必须这样做:

using (AsyncScopedLifestyle.BeginScope(container))
{
    // ...
}

但是,我的控制器中没有 container 引用,因为它全部设置为 DependencyResolver (我使用的是ASP.NET MVC 5) .


我的问题是: What is the best way of accessing Simple Injector container inside ASP.NET MVC controller?

我应该将容器实例本身注册为服务并通过控制器中的构造函数注入来获取它吗?或者我是否在它周围创建了一些包装(例如 ISimpleInjectorContainerProvider )并通过那里获取它?或者有什么办法让它通过 DependencyResolver

对不起,如果这个问题有点愚蠢,我的目标只是避免做一些可能产生负面影响的不良做法 .

1 回答

  • 2

    在ASP.NET MVC控制器中访问Simple Injector容器的最佳方法是什么?

    在控制器中访问容器的最佳方法是不这样做 .

    应用程序中唯一应该访问DI容器或其抽象(例如服务定位器或 DependencyResolver )的地方是Composition Root .

    但是,MVC控制器不是组合根的一部分,它们是表示层的一部分 . 取决于DI容器或与其相关的任何构造是一个坏主意 .

    相反,通过使用 Abstractions ,依赖注入为我们提供了一种使用decorators来拦截方法调用的方法 .

    这允许您在组合根中定义装饰器,并在将调用转发到包装服务之前让它控制范围 .

    比如你的 OrderController 取决于 IOrderService 如下:

    public class OrderController : Controller
    {
        private readonly IOrderService service;
    
        public ProductController(IOrderService service)
        {
            this.service = service;
        }
    
        [Post]       
        public ActionResult CancelOrder(Guid orderId)
        {
            this.service.CancelOrder(orderId);
    
            return this.Ok();
        }
    }
    

    如果 CancelOrder 操作需要在其自己的隔离范围内运行,而不是让 OrderController 处理它,我们应该将此职责移至另一个类 . 由于将其移入 OrderService 实现也是一个坏主意,我们可以将此行为放在装饰器中:

    // This class depends on the Container and should therefore be part of
    // your Composition Root
    public class ScopingOrderServiceDecorator : IOrderService
    {
        // Depend on the Container
        private readonly Container container;
    
        // Wrap a Func<IOrderService>. This allows the 'real' `IOrderService`
        // to be created by the container within the defined scope.
        private readonly Func<IOrderService> decorateeFactory;
    
        ScopingOrderServiceDecorator(Container container, Func<IOrderService> decorateeFactory)
        {
            this.container = container;
            this.decorateeFactory = decorateeFactory;
        }
    
        public void CancelOrder(Guid orderId)
        {
            // Create a new scope
            using (AsyncScopedLifestyle.BeginScope(this.container))
            {
                // Within the scope, invoke the factory. That ensures an instance
                // for this scope.
                IOrderService decoratee = this.decorateeFactory.Invoke();
    
                // Forward the call to the created decoratee
                decoratee.CancelOrder(orderId);
            }
        }
    }
    

    通过使用装饰器,您可以保持控制器和实际业务逻辑不变,并允许您仅通过更改组合根来添加此行为 .

    您可以在Simple Injector中注册,如下所示:

    container.Register<IOrderService, OrderService>();
    container.RegisterDecorator<IOrderService, ScopingOrderServiceDecorator>();
    

    当Simple Injector看到 SimpleInjector.Container 类型的构造函数参数时,它会自动在构造函数中注入自身 . 您没有为此进行任何特殊注册 . 但是,如上所述,只有属于Composition Root的类才应依赖于 Container ,因此不要在整个代码库中使用 Container . 这将导致代码难以维护,难以测试 .

    Simple Injector认为 Func<IOrderService> 是一种特殊的依赖 . 通常,Simple Injector不会自动注入 Func<T> 依赖项,但装饰器是此规则的例外 . Simple Injector确保注入的 Func<T> 可以解析'real'实现 .

相关问题