您是否曾经需要将作用域服务注入到单一实例服务中?
我经常需要在后台服务中解析作用域内的服务,例如 EF Core。DbContext
另一个示例是,当您需要在 ASP.NET Core 中间件中解析作用域服务时。
如果你曾经尝试过这个,你可能会遇到一个类似于这个的异常:
System.InvalidOperationException: Cannot consume scoped service 'Scoped' from singleton 'Singleton'.
今天,我将解释如何解决此问题,以及如何在 ASP.NET Core 中的单例中安全地使用作用域服务。
ASP.NET Core 有三个服务生命周期:
Transient Singleton Scoped
每次从服务容器请求临时服务时,都会创建这些服务。
作用域内服务在作用域的生存期内创建一次。对于 ASP.NET Core 应用程序,将为每个请求创建一个新范围。这是在给定请求中解析作用域内服务的方法。
ASP.NET Core 应用程序还具有用于解析单一实例服务的根。IServiceProvider
那么,如果从单一实例解析作用域内服务引发异常,我们该怎么办?
如果要在后台服务中解析作用域内的服务,该怎么办?
您可以创建一个具有其自己的实例的新作用域 ( )。作用域可用于解析作用域内的服务。释放作用域时,也会释放在该作用域内创建的所有一次性服务。IServiceScopeIServiceProviderIServiceProvider
下面是使用 创建新的 .我们正在使用作用域来解析 ,这是一个作用域服务。IServiceScopeFactoryIServiceScopeApplicationDbContext
调用时注册为单例。BackgroundJobAddHostedService
public class BackgroundJob(IServiceScopeFactory serviceScopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using IServiceScope scope = serviceScopeFactory.CreateScope();
var dbContext = scope
.ServiceProvider
.GetRequiredService<ApplicationDbContext>();
// Do some background processing with the EF database context.
await DoWorkAsync(dbContext);
}
}
如果要在 ASP.NET Core 中间件中使用作用域服务,该怎么办?
中间件在每个应用程序生命周期中构造一次。
如果尝试注入作用域内的服务,则会收到异常:
System.InvalidOperationException: Cannot resolve scoped service 'Scoped' from root provider.
有两种方法可以解决这个问题。
首先,可以使用前面的方法,使用 创建新作用域。你将能够解析作用域内的服务。但是,它们不会与同一请求中的其他作用域服务共享相同的生存期。根据您的要求,这甚至可能是一个问题。IServiceScopeFactory
有更好的方法吗?
中间件允许您在方法中注入作用域服务。注入的服务将使用当前请求的作用域,因此它们将具有与任何其他作用域服务相同的生存期。InvokeAsync
public class ConventionalMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(
HttpContext httpContext,
IMyScopedService scoped)
{
scoped.DoSomething();
await _next(httpContext);
}
}
您可能会看到使用 用于创建作用域而不是 .IServiceProviderIServiceScopeFactory
这两种方法有什么区别?
方法 from 解析实例并调用它:CreateScopeIServiceProviderIServiceScopeFactoryCreateScope()
public static IServiceScope CreateScope(this IServiceProvider provider)
{
return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
所以,如果你想直接使用 the 来创建一个范围,那很好。IServiceProvider
但是,这是实现预期结果的更直接的方法。IServiceScopeFactory
了解瞬态、作用域和单一实例生存期之间的区别对于管理 ASP.NET Core 应用程序中的依赖关系至关重要。
当您需要从单一实例解析作用域内服务时,它提供了一种解决方案。它允许您创建一个新作用域,您可以使用该作用域来解析作用域内的服务。IServiceScopeFactory
在中间件中,我们可以将作用域服务注入到方法中。这还可以确保服务使用当前请求的范围和生命周期。