【问题标题】:More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework已创建 20 多个“IServiceProvider”实例供 Entity Framework 内部使用
【发布时间】:2020-05-19 16:41:19
【问题描述】:

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

警告:Microsoft.EntityFrameworkCore.Infrastructure[10402] 已创建 20 多个“IServiceProvider”实例供 Entity Framework 内部使用。这通常是由 将新的单例服务实例注入每个 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));
                        }                         
                    },                     
                };
            }
        );
    }
}

账户服务

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

AccountService.Authorize(userName) 将在每次成功登录时被调用。等第 21 位成功用户及以后,我开始看到警告。

问题

1>在OnTokenValidated 事件中,我正在创建服务提供者并立即处理它。为什么 EF 仍在记录警告?
2>如何摆脱这个警告?

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

NET Fiddle DEMO

【问题讨论】:

    标签: entity-framework-core ef-core-2.0 ef-core-2.2 ef-core-2.1


    【解决方案1】:

    (1) 您可以定义 AddDbContext 方法的 ServiceLifetime 参数,而不是释放 dbcontext。

    上下文生命周期:服务生命周期

    在容器中注册 DbContext 服务的生命周期。

    optionsLifetime : ServiceLifetime

    在容器中注册 DbContextOptions 服务的生命周期。

    EntityFrameworkServiceCollectionExtensions.AddDbContext Method

    (2) 下面是记录器和错误陷阱的示例,可以应用于启动类中的configureservices方法。

    // define single service provider
     using (var sp = new ServiceCollection()
                       .AddLogging(l => l.AddConsole())
                       .AddDbContext<MyDBContext>(options =>
                        {
                            options.UseSqlServer("Server=(local);Database=MyDB;Integrated Security=True",
                            sqlServerOptions => sqlServerOptions.CommandTimeout(120));
                         })
                        .BuildServiceProvider())
     
     // define service logger
     using (var logger = sp.GetService<ILoggerFactory>().CreateLogger<Program>())
     {
          try
          {
              for (int i = 1; i <= 25; i++)
              {
                  using (var serviceScope = sp.CreateScope())
                  using (var accountService = sp.GetService<MyDBContext>())
                  {
                      
                  }
              }
          }
    
         // (2) catch the error warning
         catch(ex: Exception)
         {
              logger.LogInformation($@"Error: {ex.ToString()}");
         }
     }
    

    顺便说一句,EF 实体绝对是声明和运行时的静态类。因此,当数据库架构更改时,您必须修改(或执行任何迁移步骤)类。

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      通过不必要的建筑服务提供商解决了这个问题。 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));
                          }                         
                      },     
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多