简体   繁体   English

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

[英]How to access Simple Injector container inside ASP.NET MVC controller?

I'd like to experiment a little bit with the Hybrid Lifestyle in Simple Injector. 我想对Simple Injector中的Hybrid生活方式进行一些实验。 I'd like to have an optional explicitly defined Async Scope with a fallback to Web Request Scope in case the Async Scope isn't defined. 我想拥有一个可选的显式定义的Async Scope,并在未定义Async Scope的情况下回退到Web Request Scope。

I'm planning to use this inside ASP.NET MVC controllers where usually I want to resolve instances against the outermost Web Request Scope, but for some situations, I'd like to explicitly create an Async Scope inside the controller's action and have a shorter lifestyle for instances inside the scope. 我打算在ASP.NET MVC控制器中使用此方法,通常我想根据最外部的Web Request Scope解析实例,但是在某些情况下,我想在控制器的操作中显式创建一个Async Scope并使用一个较短的范围内实例的生活方式。

To explicitly create an Async Scope inside the controller's action, I have to do this: 要在控制器的动作中显式创建Async Scope,我必须这样做:

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

However, I don't have the container reference inside my controller as it's all set up with DependencyResolver (I'm using ASP.NET MVC 5). 但是,我的控制器内部没有container引用,因为它们都是通过DependencyResolver设置的(我使用的是ASP.NET MVC 5)。


My question is: What is the best way of accessing Simple Injector container inside ASP.NET MVC controller? 我的问题是: 在ASP.NET MVC控制器内访问Simple Injector容器的最佳方法什么?

Should I register the container instance itself as a service and obtain it through constructor injection in the controller? 我是否应该将容器实例本身注册为服务,并通过在控制器中进行构造函数注入来获取它? Or do I create some wrapper around it (eg ISimpleInjectorContainerProvider ) and get it through there? 还是我围绕它创建一些包装器(例如ISimpleInjectorContainerProvider )并从那里得到它? Or is there some way of getting it through DependencyResolver ? 还是有某种方法可以通过DependencyResolver获得它?

Sorry if this question is a bit stupid, my goal is just to avoid doing some bad practice that could have some negative implications. 很抱歉,如果这个问题有点愚蠢,我的目标只是避免做一些可能带来负面影响的不良做法。

What is the best way of accessing Simple Injector container inside ASP.NET MVC controller? 在ASP.NET MVC控制器内访问Simple Injector容器的最佳方法是什么?

The best way of accessing the container within a controller is to don't. 在控制器内访问容器的最好方法是不要这样做。

The only place in your application that should access your DI Container, or an abstraction thereof (such as a Service Locator or DependencyResolver ) is the Composition Root . 组合根是应用程序中唯一应访问DI容器或其抽象(例如,服务定位器或DependencyResolver )的位置。

MVC Controllers however are not part of the Composition Root, they are part of the presentation layer. 但是,MVC控制器不是组成根的一部分,它们是表示层的一部分。 Depending on the DI Container or any construct that related to it is a bad idea. 依赖DI容器或与其相关的任何构造都是一个坏主意。

Instead, By using Abstractions , Dependency Injection provides us with a way to intercept method calls by using decorators . 相反,通过使用抽象 ,依赖注入为我们提供了一种通过使用装饰器来拦截方法调用的方法。

This allows you to define a decorator inside the Composition Root, and let it control scoping, before forwarding the call to the wrapped service. 这样,您可以在将调用转发到打包服务之前, “合成根” 内部定义一个装饰器,并使其控制作用域。

Say for instance your OrderController depends on an IOrderService as follows: 假设您的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();
    }
}

In case the CancelOrder operation needs to run in its own isolated scope, instead of letting the OrderController handle this, we should move this responsibility to a different class. 如果CancelOrder操作需要在其自己的隔离范围内运行,而不是让OrderController处理此问题,则应将此职责移至其他类。 Since moving it into the OrderService implementation is a bad idea as well, we can place this behavior in a decorator: 由于将其移动到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);
        }
    }
}

By using a decorator you leave both the controller and the actual business logic untouched, and allows you to add this behavior just by making changes to your Composition Root. 通过使用装饰器,您可以使控制器和实际业务逻辑都保持不变,并允许您仅通过更改“合成根”来添加此行为。

You can register this in Simple Injector as follows: 您可以按照以下步骤在Simple Injector中进行注册:

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

Simple Injector will automatically inject itself in a constructor when it sees a constructor argument of type SimpleInjector.Container . 当Simple Injector看到类型为SimpleInjector.Container的构造函数参数时,它将自动将自身插入构造函数。 You don't have make any special registrations for that. 您没有为此进行任何特殊注册。 However, as stated above, only classes that are part of your Composition Root should take a dependency on the Container , so don't go sprinkling the Container throughout your code base. 但是,如上所述,只有属于您的“合成根”的类才应依赖Container ,因此不要在整个代码库中随意使用Container That will lead to code that is hard to maintain, and hard to test. 这将导致难以维护和测试的代码。

Simple Injector considers the Func<IOrderService> to be a special kind of dependency. Simple Injector认为Func<IOrderService>是一种特殊的依赖关系。 Typically, Simple Injector will not inject Func<T> dependencies automatically, but decorators are the exception to this rule. 通常,简单注入器不会自动注入Func<T>依赖项,但装饰器是该规则的例外。 Simple Injector ensures that the injected Func<T> can resolve the 'real' implementation. 简单注入器可确保注入的Func<T>可以解析“真实”实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM