Kei*_*ith 1 c# dependency-injection asp.net-core
我有一个使用大量依赖注入的 ASP.NET Core 项目。
问题是这些开始堆积在我的控制器动作上:
public async Task LoginAsync(
[FromBody] LoginModel login,
[FromServices] IConnectionMultiplexer redis,
[FromServices] ISerialiserFactory serialiser,
[FromServices] IDataService dataService,
[FromServices] ILookupNormalizer normaliser,
[FromServices] IPasswordHasher hasher,
...
Run Code Online (Sandbox Code Playgroud)
我可以将它们放在构造函数中,但大多数方法不使用它们,而那些使用它们的方法并不总是使用所有这些。
我可以直接实例化它们,但是我失去了在启动时注入它们的能力。
有没有更简单的方法来获得这些注入的服务?理想情况下,我想调用类似的东西:
// It turns out I need the injected serialiser
var serialiser = services.Get<ISerialiserFactory>();
Run Code Online (Sandbox Code Playgroud)
有没有办法在 ASP.NET Core 中做到这一点?
正如评论中指出的那样,如果您在单个控制器操作中拥有如此多的依赖项,那么这是对抽象错误的代码的一个很好的感叹:您的控制器做得比它应该做的要多。
理想情况下,控制器动作每个动作应该只有几行代码(经验法则,10-15 行代码)。如果你有更多,你可能在里面做了很多。
控制器操作应该只接受来自用户(表单或 WebApi 风格)的输入,验证它并将其委托给服务以及处理 http 状态代码。
IE
public class LoginService : ILoginService
{
public IConnectionMultiplexer redis,
public ISerialiserFactory serialiser,
public IDataService dataService,
public ILookupNormalizer normaliser,
public IPasswordHasher hasher
public LoginService(/* inject your services here */)
{
}
public async Task<bool> Login(LoginModel login)
{
// Do your logic here and perform the login
return /*true or false*/;
}
}
Run Code Online (Sandbox Code Playgroud)
然后将其注入您的控制器或您的操作:
[HttpPost]
public async Task<IActionResult> LoginAsync([FromBody]LoginModel login, [FromServices]ILoginService loginService)
{
// Validate input, only rough validation. No business validation here
if(!Model.IsValid)
{
return BadRequest(Model);
}
bool success = await loginService.Login(model);
if(success)
{
return RedirectTo("Login");
}
return Unauthorized();
}
Run Code Online (Sandbox Code Playgroud)
如果你得到更多的代码,那就是代码异味。特别是如果你做一些逻辑等。你的控制器应该尽可能薄。控制器很难测试(与ILoginService我的示例相比)。
您永远不必new LoginService(...)在任何时候调用(除非您创建了一个抽象工厂)。
此外,您应该始终更喜欢使用构造函数注入。[FromServices]仅在一个操作中需要服务时使用。如果在多个操作中需要它,请始终使用构造函数注入
public LoginController : Controller
{
public ILoginService loginService;
public LoginController(ILoginService loginService)
{
if(loginService==null)
throw new ArgumentNullException(nameof(loginService));
this.loginService = loginService
}
public async Task<IActionResult> LoginAsync([FromBody]LoginModel login)
{
// Do your stuff from above
...
bool success = await loginService.Login(login);
...
}
}
Run Code Online (Sandbox Code Playgroud)
也没有问题,如果依赖项有不同的生命周期,只要主对象的生命周期比它的依赖项的生命周期短。
即,如果您的上述依赖项之一是有范围的,那么您也ILoginService必须是有范围的。它将在请求结束时处理。
services.AddSingleton<ISerialiserFactory, ...>();
services.AddSingleton<IConnectionMultiplexer, ...>();
services.AddScoped<IDataService, ...>();
services.AddScoped<ILookupNormalizer, ...>();
services.AddScoped<IPasswordHasher, ...>();
services.AddScoped<ILoginService, LoginService>();
Run Code Online (Sandbox Code Playgroud)
那会工作得很好。
services.AddSingleton<ISerialiserFactory, ...>();
services.AddSingleton<IConnectionMultiplexer, ...>();
services.AddScoped<IDataService, ...>();
services.AddScoped<ILookupNormalizer, ...>();
services.AddScoped<IPasswordHasher, ...>();
// This will create trouble
services.AddSingleton<ILoginService, LoginService>();
Run Code Online (Sandbox Code Playgroud)
但这不会。现在, ILoginService 将是单例的,但它的依赖项将在第一个请求后被释放。后续请求会在调用IDataService或IPasswordHasher...“xyz 已被处理”时触发异常。
| 归档时间: |
|
| 查看次数: |
2060 次 |
| 最近记录: |