1 回答
TA贡献1942条经验 获得超3个赞
为了了解发生了什么,您首先必须了解依赖注入生命周期之间的差异:
瞬态:为每个解决的依赖关系创建一个新实例。
Singleton:每当服务得到解决时,就会使用单个共享实例。
范围:每当服务在单个范围(或请求)内得到解析时,就会共享单个实例。后续请求将意味着将再次创建一个新实例。
数据库上下文保存与数据库的连接。这就是为什么您通常不希望它成为单例,这样您就不会在应用程序的整个生命周期中保持单个连接打开。所以你想让它成为暂时的。但是,如果您需要在处理单个请求时多次访问数据库,则需要在短时间内多次打开数据库连接。因此,折衷方案是默认使其成为范围依赖项:这样您就不会长时间保持连接打开,但您仍然可以在短时间内重用连接。
现在,让我们考虑一下当单例服务依赖于非单例服务时会发生什么:单例服务只创建一次,因此它的依赖关系也只解决一次。这意味着它所具有的任何依赖项现在都在该服务的整个生命周期(即应用程序的生命周期)中有效共享。因此,通过依赖非单例服务,您可以有效地使这些服务成为准单例。
这就是为什么(在开发期间)存在保护措施,可以防止您犯此错误:作用域验证将检查您是否不依赖于作用域之外的作用域服务,例如在单例服务内。这样,您就不会逃避该范围服务的预期生命周期。
当您现在AppIdentityDbContext.CreateAdminAccount
在该方法内运行时Configure
,您是在范围之外运行它。所以你基本上处于“单身土地”内。您现在创建的任何依赖项都将保留。由于您解析了UserManager<AppUser>
和RoleManager<IdentityRole>
两者都依赖于作用域数据库上下文,因此您现在正在转义数据库上下文的配置作用域生命周期。
为了解决这个问题,您应该创建一个短暂的作用域,然后您可以在其中访问作用域服务(因为您位于作用域内),当作用域终止时,这些服务将被正确清理:
public static async Task CreateAdminAccount(IServiceProvider serviceProvider, IConfiguration configuration)
{
// get service scope factory (you could also pass this instead of the service provider)
var serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
// create a scope
using (var scope = serviceScopeFactory.CreateScope())
{
// resolve the services *within that scope*
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<AppUser>>();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
// do stuff
}
// scope is terminated after the using ends, and all scoped dependencies will be cleaned up
}
- 1 回答
- 0 关注
- 75 浏览
添加回答
举报