为了账号安全,请及时绑定邮箱和手机立即绑定

禁用 Program 类中的依赖项注入范围验证功能?

禁用 Program 类中的依赖项注入范围验证功能?

C#
隔江千里 2023-09-24 10:43:45
我的教科书展示了一个构建身份服务的示例,下面是代码://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.cspublic 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"];            }        }    }}然后教科书上说:因为我通过 IApplicationBuilder.ApplicationServices 提供程序访问作用域服务,所以我还必须禁用 Program 类中的依赖项注入作用域验证功能,如下所示://Program.cspublic static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseDefaultServiceProvider(options => options.ValidateScopes = false) .Build();我对 DI 有基本的了解,但我对这个例子很困惑,以下是我的问题:Q1-通过 IApplicationBuilder.ApplicationServices 提供程序访问范围服务 是什么意思?它尝试访问哪些服务?为什么它的作用域不是瞬态的或单例的?Q2-为什么我们必须禁用依赖注入范围验证,范围验证试图实现什么目的?
查看完整描述

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

}


查看完整回答
反对 回复 2023-09-24
  • 1 回答
  • 0 关注
  • 75 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信