猿问

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

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


//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"];


            }

        }

    }

}

然后教科书上说:


因为我通过 IApplicationBuilder.ApplicationServices 提供程序访问作用域服务,所以我还必须禁用 Program 类中的依赖项注入作用域验证功能,如下所示:


//Program.cs

public static IWebHost BuildWebHost(string[] args) =>

 WebHost.CreateDefaultBuilder(args)

 .UseStartup<Startup>()

 .UseDefaultServiceProvider(options => options.ValidateScopes = false)

 .Build();

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


Q1-通过 IApplicationBuilder.ApplicationServices 提供程序访问范围服务 是什么意思?它尝试访问哪些服务?为什么它的作用域不是瞬态的或单例的?


Q2-为什么我们必须禁用依赖注入范围验证,范围验证试图实现什么目的?


隔江千里
浏览 76回答 1
1回答

手掌心

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