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

一曲冷凌霜 提交于 2019-12-04 13:40:55

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