gpt4 book ai didi

c# - 在 Program 类中禁用依赖注入(inject)范围验证功能?

转载 作者:行者123 更新时间:2023-11-30 15:13:37 25 4
gpt4 key购买 nike

我的教科书展示了一个构建身份服务的例子,下面是代码:

//startup.cs    
public void Configure(IApplicationBuilder app) {
app.UseStatusCodePages();
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
//try to seed an admin account for the first time the app runs
AppIdentityDbContext.CreateAdminAccount(app.ApplicationServices, Configuration).Wait();
}


//AppIdentityDbContext.cs
public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options) : base(options) { }

public static async Task CreateAdminAccount(IServiceProvider serviceProvider, IConfiguration configuration)
{
UserManager<AppUser> userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
RoleManager<IdentityRole> roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string username = configuration["Data:AdminUser:Name"];
string email = configuration["Data:AdminUser:Email"];
string password = configuration["Data:AdminUser:Password"];
string role = configuration["Data:AdminUser:Role"];

if (await userManager.FindByNameAsync(username) == null)
{
if (await roleManager.FindByNameAsync(role) == null)
{
await roleManager.CreateAsync(new IdentityRole(role));
}
AppUser user = new AppUser
{
UserName = username,
Email = email
};
IdentityResult result = await userManager.CreateAsync(user, password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(user, role);
}
}
}
}

然后课本上说:

Because I am accessing a scoped service via the IApplicationBuilder.ApplicationServices provider, I must also disable the dependency injection scope validation feature in the Program class, as shown below:

//Program.cs
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseDefaultServiceProvider(options => options.ValidateScopes = false)
.Build();

我对 DI 有基本的了解,但是我对这个例子真的很困惑,下面是我的问题:

Q1- 通过 IApplicationBuilder.ApplicationServices 提供程序访问范围内的服务这是什么意思?它试图访问哪些服务?为什么它的范围不是 transient 或单例?

Q2- 为什么我们必须禁用依赖注入(inject)作用域验证,作用域验证试图达到什么目的?

最佳答案

为了理解发生了什么,您首先必须理解依赖注入(inject)生命周期之间的区别:

  • transient :为每个已解决的依赖项创建一个新实例。
  • 单例:只要服务得到解析,就会使用单个共享实例。
  • Scoped:只要服务在单个范围(或请求)内得到解析,就会共享单个实例。后续请求将意味着将再次创建一个新实例。

数据库上下文持有到数据库的连接。这就是为什么你通常不希望它是单例的,这样你就不会在应用程序的整个生命周期中保持一个连接打开。所以你会想让它成为短暂的。但是,如果您需要在为单个请求提供服务时多次访问数据库,您将在短时间内多次打开数据库连接。因此折衷方案是使其成为默认范围内的依赖项:这样您就不会长时间保持连接打开状态,但您仍然可以在短时间内重用该连接。

现在,让我们考虑一下当单例服务依赖于非单例服务时会发生什么:单例服务只创建一次,因此它的依赖项也只解析一次。这意味着它拥有的任何依赖性现在都可以在该服务的整个生命周期内有效地共享——也就是应用程序的生命周期。因此,通过依赖非单例服务,您可以有效地使这些服务成为准单例。

这就是为什么(在开发过程中)有一个保护机制在起作用,它可以防止您犯下这个错误:范围验证将检查您是否不依赖于范围之外的范围服务,例如在单例服务中。这样,您就不会逃避该作用域服务的预期生命周期。

当你现在运行 AppIdentityDbContext.CreateAdminAccountConfigure 内方法,您正在范围之外运行它。所以你基本上处于“单例之地”。您现在创建的任何依赖项都将保留。既然你解决了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
}

关于c# - 在 Program 类中禁用依赖注入(inject)范围验证功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57651750/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com