C# 如何访问ASP.NET MVC控制器中的简单注射器容器?

C# 如何访问ASP.NET MVC控制器中的简单注射器容器?,c#,asp.net-mvc,ioc-container,simple-injector,C#,Asp.net Mvc,Ioc Container,Simple Injector,我想用简单的注射器来试验一下混合生活方式。我希望有一个可选的显式定义的异步作用域,在没有定义异步作用域的情况下,可以回退到Web请求作用域 我计划在ASP.NET MVC控制器中使用它,通常我希望根据最外层的Web请求作用域解析实例,但在某些情况下,我希望在控制器的操作中显式创建一个异步作用域,并为作用域内的实例提供更短的生活方式 要在控制器的操作内显式创建异步作用域,我必须执行以下操作: using (AsyncScopedLifestyle.BeginScope(container)) {

我想用简单的注射器来试验一下混合生活方式。我希望有一个可选的显式定义的异步作用域,在没有定义异步作用域的情况下,可以回退到Web请求作用域

我计划在ASP.NET MVC控制器中使用它,通常我希望根据最外层的Web请求作用域解析实例,但在某些情况下,我希望在控制器的操作中显式创建一个异步作用域,并为作用域内的实例提供更短的生活方式

要在控制器的操作内显式创建异步作用域,我必须执行以下操作:

using (AsyncScopedLifestyle.BeginScope(container))
{
    // ...
}
但是,我的控制器中没有
容器
引用,因为它都是用
依赖解析程序
设置的(我使用的是ASP.NET MVC 5)


我的问题是:访问ASP.NET MVC控制器内简单注射器容器的最佳方式是什么?

我是否应该将容器实例本身注册为服务,并通过控制器中的构造函数注入获得它?或者我应该在它周围创建一些包装器(例如,
ISimpleInjectorContainerProvider
)并通过它?或者是否有某种方法可以通过依赖解析程序获得它

对不起,如果这个问题有点愚蠢的话,我的目标就是避免做一些可能带来负面影响的坏习惯

访问ASP.NET MVC控制器内的简单注射器容器的最佳方式是什么

在控制器中访问容器的最佳方法是不要

应用程序中唯一应该访问DI容器或其抽象(例如服务定位器或
依赖解析程序
)的地方是

然而,MVC控制器不是合成根的一部分,它们是表示层的一部分。依赖于DI容器或与之相关的任何构造都不是一个好主意

相反,通过使用抽象,依赖项注入为我们提供了一种通过使用截获方法调用的方法

这允许您在组合根中定义一个decorator,并让它在将调用转发到包装服务之前控制作用域

例如,假设您的
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();
    }
}
container.Register<IOrderService, OrderService>();
container.RegisterDecorator<IOrderService, ScopingOrderServiceDecorator>();
如果
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);
        }
    }
}
//此类依赖于容器,因此应该是
//你的作文是根
公共类作用域OrderServiceDecorator:IOrderService
{
//依靠容器
专用只读容器;
//包装一个Func。这允许“real”IOrderService`
//由容器在定义的范围内创建。
私有只读Func装饰工厂;
ScopingOrderServiceDecorator(容器容器,Func DecorateFactory)
{
this.container=容器;
this.decorateFactory=decorateeFactory;
}
公共作废取消订单(Guid orderId)
{
//创建一个新范围
使用(AsyncScopedLifestyle.BeginScope(this.container))
{
//在范围内,调用工厂。这样可以确保
//对于这个范围。
IOrderService decoratee=this.decorateeFactory.Invoke();
//将调用转发给已创建的装饰对象
decoree.CancelOrder(orderId);
}
}
}
通过使用decorator,您可以使控制器和实际业务逻辑保持不变,并允许您仅通过更改组合根来添加此行为

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

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();
    }
}
container.Register<IOrderService, OrderService>();
container.RegisterDecorator<IOrderService, ScopingOrderServiceDecorator>();
container.Register();
container.RegisterDecorator();
当Simple Injector看到类型为
SimpleInjector.Container
的构造函数参数时,它将自动将自己注入构造函数。你不必为此做任何特别注册。但是,如上所述,只有作为组合根的一部分的类才应该依赖于
容器
,因此不要将
容器
散布到整个代码库中。这将导致代码难以维护和测试


简单注入器将
Func
视为一种特殊的依赖项。通常,简单注入器不会自动注入
Func
依赖项,但装饰器是此规则的例外。Simple Injector确保注入的
Func
可以解决“真实”实现。

您能否提供更多关于为什么需要嵌套范围的上下文,并显示需要它的代码?我的计划是为每个业务层事务都有一个嵌套范围。通常每个web请求都有零个或一个事务。但有时我希望在一个web请求中一个接一个地执行多个事务(并不经常需要)。对我来说,将一些对象的作用域仅限于业务层事务是有意义的,因此它们的生命周期与事务一起开始/结束。现在,我不使用这种方法,所以我没有代码来显示。但我想尝试一下,看看它是否有意义,尝试一下……谢谢你。我认为将所有DI逻辑与其他逻辑分开是一个更好的主意,这看起来是一个很好的解决方案。我得花点时间来考虑这个想法。有时候,诀窍是换一种思维方式。请注意,我的答案实际上可能不是您案例的最佳解决方案。我的回答受限于对您的问题和obv的理解