ServiceScopeFactory的使用方式
上篇文章谈到在项目中遇到了一个问题,其实就是生命周期的问题,在构造函数的参数中使用了scope生命周期的服务,然后使用ServiceScopeFactory去解决这一问题 ,但是使用方式好像不太正确
上篇文章说遇到了问题,ServiceScopeFactory使用方式好像不太正确,为什么?因为构造这个管道的原计划是针对于每一个请求,但是现在好像变成了单例,代码如下:
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IServiceScopeFactory _scopeFactory;
private readonly IServiceScope serviceScope;
private readonly IMyService _service;
public MyMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
{
_next = next;
_scopeFactory = scopeFactory;
serviceScope = _scopeFactory.CreateScope();
_service = serviceScope.ServiceProvider.GetRequiredService<IMyService>()
}
public async Task InvokeAsync(HttpContext httpContext)
{
// 在这里使用 myService 做一些事情
myService.Handle(httpContext);
// 调用下一个中间件
await _next(httpContext);
// 注意:一旦离开 using 块,服务范围将被释放,并且所有通过该范围解析的服务也将被释放
}
}
如上所示代码,虽然也能跑,但是却搞不好就会有并发问题,因为所有的请求都是用的同一个_service,而我们本意是针对于每个httpcontext,都获取一个服务。但是也能跑 :)
而实际上我们想写的是如下的代码:
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IServiceScopeFactory _scopeFactory;
public MyMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
{
_next = next;
_scopeFactory = scopeFactory;
}
public async Task InvokeAsync(HttpContext httpContext)
{
// 创建一个新的服务范围
using (var scope = _scopeFactory.CreateScope())
{
// 从服务范围中解析你需要的服务
var myService = scope.ServiceProvider.GetRequiredService<IMyService>();
// 在这里使用 myService 做一些事情
// 调用下一个中间件
await _next(httpContext);
}
// 注意:一旦离开 using 块,服务范围将被释放,并且所有通过该范围解析的服务也将被释放
}
}
这意味着服务的实例是在每个请求的基础上创建的。这在处理依赖关系注入(DI)时特别有用,当你希望为每个请求创建新的服务实例时。
总结
两种方式都不能算错,主要是看用在什么情况下,如果业务较为复杂,有并发问题,当然还是下面的写法为好。
这里主要是记录一下当时脑抽写的代码。