为什么通过HttpContext.RequestServices
或IServiceProvider
请求服务会考虑不良做法。我可以在这个地方读到这句话:
建议使用构造函数注入而不是获取它 使用RequestServices。
我的想法恰恰相反。尽可能使用RequestServices。让我解释一下原因。 我想保持控制器尽可能小,所以我创建了单独的服务。这样,我的控制器很干净:
public class MyController : Controller
{
public MyController(IMyServices MyServices){}
public async Task<IActionResult> GetSomething(int parameter)
{
return await this.MyServices.SomeMethod(parameter);
}
}
所有服务都从base继承,其中包含管理权限的逻辑,缓存sql请求....... 使用构造函数方法,我得到了非常复杂的调用库系统:
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
我简化了构造函数的调用:
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
)。
如果我反对构造函数方法。如果生命周期为Scoped,如果为MyServices调用ServiceProvider.GetRequiredService
,是否会有任何性能损失。
答案 0 :(得分:6)
为什么通过HttpContext.RequestServices或IServiceProvider请求服务会考虑不良做法。
它被认为是一种不好的做法,因为它是Service Locator anti-pattern的实现。
您试图通过将许多常见的依赖项和公共逻辑移动到基类中来保持控制器类的小,但这本身就是一种反模式,因为:
基类使用继承,而软件开发的一般共识是你应该支持Composition over inheritance。
由于您应该支持合成,这会自动导致依赖注入。如果没有基类,就会更容易发现单一责任原则违规,因为您的构造函数将获得更多参数。再次注意,使用服务定位器时,依赖项的数量不会改变,它们更难计算。
你应该接受这样一个事实,即构造函数注入很容易导致Constructor over-injection,因为这表明我们的类做得太多,这是一个我们应该重新设计这样的类的信号。
不是在基类中实现cross-cutting concerns(如日志记录,缓存和授权),而是使用组合实现它们。例如,您可以使用middleware,decorators或interceptors将横切关注点应用于请求(或此类请求的特定服务)。