已创建二十多个“IServiceProvider”实例供实体框架内部使用

LP1*_*P13 5 entity-framework-core ef-core-2.0 ef-core-2.1 ef-core-2.2

我在 ASP.NET Core 2.2 应用程序中收到此警告

警告:Microsoft.EntityFrameworkCore.Infrastruct[10402] 已创建 20 多个“IServiceProvider”实例供实体框架内部使用。这通常是由于将新的单例服务实例注入每个 DbContext 实例而引起的。例如,调用 UseLoggerFactory 每次都会传入一个新实例 - 有关更多详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=869049 。考虑检查对“DbContextOptionsBuilder”的调用,这可能需要构建新的服务提供程序。

花了一些时间后我发现它发生在startup.cs中。我正在使用 IdentityServer3 + OpenIDCnnection 进行身份验证。

用户成功登录后,客户端应用程序对用户进行授权,确保用户存在于客户端应用程序的数据库中。

客户端应用程序的Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IAccountService, AccountService>();
        services.AddDbContext<Data.Entities.MyDBContext>(options =>
        {
            options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"), 
                sqlServerOptions => sqlServerOptions.CommandTimeout(sqlCommandTimeout));
        });
    
        services.AddAuthentication(options =>
            {
                // removed for brevity purpose
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            {
                // removed for brevity purpose
            })
            .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
            {
           
                options.Events = new OpenIdConnectEvents()
                {
                    OnTokenValidated = async context =>
                    {
                        Data.Entities.UserAccount userAccount = null;
                        using (var serviceProvider = services.BuildServiceProvider())
                        {
                            using (var serviceScope = serviceProvider.CreateScope())
                            {
                                using (var accountService = serviceScope.ServiceProvider.GetService<IAccountService>())
                                {
                                    userAccount = await accountService.Authorize(userName);
                                }
                            }
                        }

                        if (userAccount == null)
                        {
                            throw new UnauthorizedAccessException(string.Format("Could not find user for login '{0}' ", userName));
                        }                         
                    },                     
                };
            }
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

账户服务

public class AccountService : IAccountService
{
    private bool _disposed = false;
    private readonly MyDBContext_dbContext;

    public AccountService(MyDBContext dbContext)
    {
        _dbContext = dbContext;     
    }

    public UserAccount Authorize(string userName)
    {
        // Ensures user exists in the database
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            if (_dbContext != null)
            {
                _dbContext.Dispose();
            }
            // Free any other managed objects here.                    
        }

        // Free any unmanaged objects here.
        _disposed = true;
    }   
}
Run Code Online (Sandbox Code Playgroud)

AccountService.Authorize(userName)每次成功登录都会被调用。等第 21 个成功用户之后,我开始看到警告。

问题

1>OnTokenValidated如果我正在创建服务提供商并立即处置它。为什么 EF 仍然出现日志警告?
2>如何消除这个警告?

即使我使用范围创建 20+ DBContext,我也会收到此警告

网络小提琴演示

小智 9

扩展一些其他答案 - 如果您始终使用新的 .Npgsql,则可能会发生这种情况NpgsqlDataSourceBuilder

前:

services.AddDbContext<DevaQueueDbContext>((options) => { 
    var dbDataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("Database"));
    options.UseNpgsql(dbDataSourceBuilder.Build());
});
Run Code Online (Sandbox Code Playgroud)

后:

var dbDataSource = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("Database")).Build();

services.AddDbContext<DevaQueueDbContext>((options) => { 
    options.UseNpgsql(dbDataSource);
});
Run Code Online (Sandbox Code Playgroud)


LP1*_*P13 0

通过不必要的建筑服务提供商解决了这个问题。参数context有HttpContext。并且HttpContext提供对ServiceProvider的访问

                OnTokenValidated = async context =>
                {
                    Data.Entities.UserAccount userAccount = null;
                    
                    using (var accountService = context.HttpContext.RequestServices.GetService<IAccountService>())
                    {
                       userAccount = await accountService.Authorize(userName);
                    }
                        

                    if (userAccount == null)
                    {
                        throw new UnauthorizedAccessException(string.Format("Could not find user for login '{0}' ", userName));
                    }                         
                },     
Run Code Online (Sandbox Code Playgroud)