disable the dependency injection scope validation feature in the Program class?

守給你的承諾、 提交于 2019-12-06 06:48:36

问题


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

and then the textbook says:

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();

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

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- why we have to disable the dependency injection scope validation, what does scope validation try to achieve?


回答1:


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

  • 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.

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
}


来源:https://stackoverflow.com/questions/57651750/disable-the-dependency-injection-scope-validation-feature-in-the-program-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!