禁用Program类中的依赖项注入范围验证功能? [英] disable the dependency injection scope validation feature in the Program class?

查看:98
本文介绍了禁用Program类中的依赖项注入范围验证功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My textbook shows an example to build identity services, below is the code:

//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);
            }
        }
    }
}

然后教科书说:

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

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有一个基本的了解,但是我对此示例确实感到困惑,以下是我的问题:

I have a basic understanding in DI, but I'm really confused with this example, below are my questions:

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

Q1- accessing a scoped service via the IApplicationBuilder.ApplicationServices provider what does it mean? what services it tries to access? why it is scoped not transient or singleton?

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

Q2- why we have to disable the dependency injection scope validation, what does scope validation try to achieve?

推荐答案

为了了解正在发生的事情,您首先必须了解依赖项注入生存期之间的区别:

In order to understand what is going on, you will first have to understand the difference between the dependency injection lifetimes:

  • 瞬态:为每个已解决的依赖项创建一个新实例.
  • Singleton:服务解析后,将使用单个共享实例.
  • 作用域:只要在单个作用域(或请求)中解析服务,就共享一个实例..后续请求将意味着将再次创建一个新实例.
  • Transient: A new instance gets created for every dependency that gets resolved.
  • Singleton: A single shared instance is used whenever the service gets resolved.
  • Scoped: A single instance is shared whenever the service gets resolved within a single scope (or request). A subsequent request will mean that a new instance will be created again.

数据库上下文包含与数据库的连接.这就是为什么通常不希望它是单例的原因,这样您就不会在应用程序的整个生命周期中保持单个连接打开.因此,您需要使其瞬态.但是,如果在处理单个请求时需要多次访问数据库,那么您将在短时间内多次打开数据库连接.因此,折衷方案是在默认情况下使其成为作用域的依赖项:这样一来,您不必长时间保持连接打开状态,但仍可以在短时间内重用该连接.

A database context holds a connection to the database. That’s why you usually don’t want it to be a singleton, so that you don’t keep a single connection open for the whole lifetime of your application. So you would want to make it transient. But then, if you needed to access the database multiple times while serving a single request, you would be opening the database connection multiple times within a short duration. So the compromise is to make it a scoped dependency by default: That way you don’t keep the connection open for a long time, but you also can still reuse the connection for a short duration.

现在,让我们考虑当单例服务依赖于非单例服务时会发生什么:单例服务仅创建一次,因此其依赖项也只能解析一次.这意味着它所具有的任何依赖关系现在都可以在该服务的整个生命周期(即应用程序的生命周期)中有效地共享.因此,通过依赖非单一服务,您可以有效地使这些服务成为准单一服务.

Now, let’s think about what happens when a singleton service depends on a non-singleton service: The singleton service gets created just once, so its dependencies are also only resolved once. That means that any dependency it has is now effectively shared throughout the lifetime of that service—which is the lifetime of the application. So by depending on non-singleton services, you effectively make those services quasi-singleton.

这就是在开发过程中(在开发过程中)提供保护的原因,可以防止您犯此错误:作用域验证将检查您是否不依赖作用域之外的作用域服务,例如在单例服务中.这样,您就无法逃脱该范围服务的预期寿命.

That’s why there is a protection in play (during development), that protects you from making this mistake: The scope validation will check that you are not depending on scoped services outside of scopes, e.g. within singleton services. That way, you are not escaping the desired lifetime of that scoped service.

当您现在在Configure方法中运行AppIdentityDbContext.CreateAdminAccount时,您正在范围之外运行它.因此,您基本上位于单身土地"内.您现在创建的任何依赖项都将保留.由于您解析了都依赖于作用域数据库上下文的UserManager<AppUser>RoleManager<IdentityRole>,因此现在可以转义数据库上下文的已配置作用域生存期.

When you now run AppIdentityDbContext.CreateAdminAccount within the Configure method, you are running this outside of a scope. So you are basically within "singleton land". Any dependency you now create will be kept around. Since you resolve UserManager<AppUser> and RoleManager<IdentityRole> which both depend on the scoped database context, you are now escaping the database context’s configured scoped lifetime.

为了解决此问题,您应该创建一个短暂的作用域,然后您可以在其中访问作用域服务(由于您在作用域之内),因此在作用域终止时将对其进行适当清理:

In order to fix this, you should create a short-lived scope in which you can then access scoped services (since you are within a scope) that will be properly cleaned up when the scope terminates:

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
}

这篇关于禁用Program类中的依赖项注入范围验证功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆