已创建 20 多个 'IServiceProvider' 实例供实体框架内部使用



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

警告: Microsoft.EntityFrameworkCore.Infrastructure[10402] 已经创建了二十多个"IServiceProvider"实例供实体框架内部使用。这通常是由以下原因引起的 将新的单一实例服务实例注入到每个 DbContext 中 实例。例如,调用 UseLoggerFactory 传入一个新的 每次实例 - 请参阅 https://go.microsoft.com/fwlink/?linkid=869049 了解更多详情。考虑在 "DbContextOptionsBuilder",可能需要新的服务提供商 建立。

花了一些时间后,我弄清楚了它在初创公司.cs中发生的事情。我正在使用IdentityServer3 + OpenIDCnnection进行身份验证。

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

客户端应用程序的启动.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,我也会收到此警告

网络小提琴演示

扩展其他一些答案 - 如果您总是使用新NpgsqlDataSourceBuilder,Npgsql 可能会发生这种情况。

以前:

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

后:

var dbDataSource = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("Database")).Build();
services.AddDbContext<DevaQueueDbContext>((options) => { 
options.UseNpgsql(dbDataSource);
});

鉴于这是此异常的最高结果,对于那些遇到相同异常但处于不同情况的人来说,请检查此处给出的答案:

测试期间的 EF Core 内部缓存和许多数据库上下文类型

具体到这里答案中的解释:https://stackoverflow.com/a/70504238/402185

而我在这里遇到的具体情况是:https://stackoverflow.com/a/76212845/402185

总结:如果您的DbContextOptionsBuilder在每次创建数据上下文时都会产生不同的选项,则最终会遇到此异常。就我而言,我通过在OnConfiguring方法中实例化interceptor向我的optionsBuilder添加了一个。

相反,将侦听器的创建移动到静态变量,以确保使用的侦听器实例始终相同。

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

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

用于在容器中注册 DbContext 服务的生存期。

选项寿命 : 使用寿命

用于在容器中注册 DbContextOptions 服务的生存期。

EntityFrameworkServiceCollectionExtensions.AddDbContext Method

(2(波纹管是记录器和错误陷阱的示例,可以应用于启动类中的配置服务方法。

// 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 实体绝对是声明和运行时的静态类。因此,当数据库架构更改时,必须修改(或执行任何迁移步骤(类。

希望这有帮助。

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

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

最新更新