ASP.NET Core DI 构造函数与 RequestServices [英] ASP.NET Core DI Constructor vs RequestServices

查看:57
本文介绍了ASP.NET Core DI 构造函数与 RequestServices的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么通过 HttpContext.RequestServicesIServiceProvider 请求服务被认为是不好的做法.我到处都能读到这个声明:

Why is requesting services via HttpContext.RequestServices or IServiceProvider consider bad practise. I can read this statement all over the place:

建议使用构造函数注入而不是获取使用请求服务.

It is recommended to use constructor injection instead of getting it using RequestServices.

<小时>

我的想法正好相反.尽可能使用 RequestServices.让我解释一下原因.我想让控制器尽可能小,所以我创建了单独的服务.这样,我的控制器就干净了:


My idea is just the opposite. Use RequestServices where ever possible. Let me explain why. I want to keep controllers as small as possible so I create separate services. This way, my controllers are clean:

public class MyController : Controller
{
    public MyController(IMyServices MyServices){}

    public async Task<IActionResult> GetSomething(int parameter)
    {
       return await this.MyServices.SomeMethod(parameter);
    }
}

所有服务都继承自base,其中包含管理权限、缓存sql请求的逻辑....
使用构造函数方法,我得到了非常复杂的调用 base 系统:

All services inherit from base, which contains logic for managing rights, caching sql requests ... .
With constructor approach I get very complicated system of calling base:

public class MyBaseService
{
    public MyBaseService(ILogger Logger, AppDbContext Context, IMemoryCache Cache) { }

    public bool HasRight(string right) { return true; }

    public bool IsLoggedIn() { return true; }
}

public class MyServices : MyBaseService
{
    public MyServices(ILogger Logger, AppDbContext Context, IMemoryCache Cache) : base(Logger, Context, Cache)
    {    
    }
}

但是使用 GetRequiredService 我简化了基于构造函数的调用:

but with GetRequiredService I simplify calling base on constructor:

public class MyBaseService2
{
    private ServiceProvider _ServiceProvider;
    public MyBaseService2(IServiceProvider ServiceProvider)
    {

    }

    protected ILogger Logger { get { return this._ServiceProvider.GetRequiredService<ILogger>(); } }

    protected AppDbContext Context { get { return this._ServiceProvider.GetRequiredService<AppDbContext>(); } }

    protected IMemoryCache Cache { get { return this._ServiceProvider.GetRequiredService<IMemoryCache>(); } }

    public bool HasRight(string right) { return true; }

    public bool IsLoggedIn() { return true; }
}

public class MyServices2 : MyBaseService2
{
    public MyServices2(IServiceProvider ServiceProvider) : base(ServiceProvider)
    {    
    }
}

是的,BaseService 包含更多的代码,但是当我在 BaseService 中需要额外的服务时,没有必要修复每个类的基构造函数调用.此外,我所有的服务都有更简单的构造函数(只是 IServiceProvider).

Yes, BaseService contains more code, but when I need additional services in BaseService, there is no need to fix every class base constructor call. Also all my services have much simpler constructor (just IServiceProvider).

如果我反对构造函数方法.如果生命周期是 Scoped,那么如果为 MyServices 调用 ServiceProvider.GetRequiredService 是否会影响性能.

If I go against constructor approach. Is there any performance hit if calling ServiceProvider.GetRequiredService for MyServices if there lifetime is Scoped.

推荐答案

为什么通过 HttpContext.RequestServices 或 IServiceProvider 请求服务被认为是不好的做法.

Why is requesting services via HttpContext.RequestServices or IServiceProvider consider bad practise.

它被认为是一种不好的做法,因为它是 Service Locator anti-模式.

It's considered a bad practice because it is an implementation of the Service Locator anti-pattern.

您试图通过将许多公共依赖项和公共逻辑移动到基类中来保持控制器类较小,但这本身就是一种反模式,因为:

You are trying to keep your controller classes small by moving many common dependencies and common logic into the base class, but that is an anti-pattern by itself, because:

  • 即使从基类解析依赖项,这些依赖项仍然是隐藏的,如 这篇文章 解释了.这意味着依赖项对您的单元测试和 DI 容器是隐藏的,他们将无法对您的对象图进行分析.
  • 将依赖项移至基类并不会降低类的复杂性,因为基类始终与派生类强耦合.
  • 它导致许多职责被塞进基类,这将导致基类违反单责任原则.这会迫使基类成为神类并不断变化.
  • Even though dependencies are resolved from the base class, those dependencies are still hidden, as this article explains. This means dependencies are hidden from your unit tests and from the DI Container, who will be unable to do analysis on your object graphs.
  • Moving dependencies to the base class doesn't lower the complexity of a class, since the base class is always strongly coupled with the derivative.
  • It causes many responsibilities to be crammed into the base class, which will cause the base class to violate the Single Responsibility Principle. This can force the base class to become a god class and under constant change.

基类使用继承,而软件开发中的普遍共识是您应该支持组合胜于继承.

Base classes use inheritance, while the general consensus in software development is that you should favor Composition over inheritance.

既然你应该支持组合,这会自动导致依赖注入.在没有基类的情况下,发现违反单一职责原则的行为立即变得更加容易,因为您的构造函数将获得更多参数.再次注意,使用 Service Locator 时依赖项的数量不会改变,只是更难计算.

Since you should favor composition, this automatically leads to Dependency Injection. Without having a base class, it immediately becomes easier to spot Single Responsibility Principle violations, because your constructors will get more arguments. Note again that the number of dependencies doesn't change when using Service Locator, they are just harder to count.

你应该接受这样一个事实,即构造函数注入很容易导致 构造函数超过-injection,因为这表明我们的类做了太多事情,并且表明我们应该重新设计这样的类.

You should embrace the fact that Constructor Injection easily leads to Constructor over-injection, since this is an indication that our class does too much and it is a signal that we should redesign such class.

而不是实施横切关注点,例如在一个基类,使用组合实现它们.例如,您可以使用中间件装饰器拦截器 将横切关注点应用于请求(或此类请求的特定服务).

Instead of implementing cross-cutting concerns such as logging, caching, and authorization in a base class, implement them using composition. For instance, you can use middleware, decorators or interceptors to apply cross-cutting concerns to a request (or a specific service of such request).

这篇关于ASP.NET Core DI 构造函数与 RequestServices的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆