测试期间的 EF Core 内部缓存和许多 DbContext 类型

lon*_*nix 4 c# integration-testing unit-testing entity-framework-core ef-core-5.0

我有很多个测试班,每个班有几十个测试。我想隔离测试,所以MyDbContext我不使用大型上下文,而是使用MyDbContextToTestFoo, MyDbContextToTestBar,MyDbContextToTestBaz等。所以我有很多子DbContext类。

在我使用 EF Core 5 进行的单元测试中,我遇到了ManyServiceProvidersCreatedWarning. 它们单独工作,但许多在作为组运行时失败:

System.InvalidOperationException:警告“Microsoft.EntityFrameworkCore.Infrastruct.ManyServiceProvidersCreatedWarning”生成错误:已创建超过二十个“IServiceProvider”实例供实体框架内部使用。这通常是由于将新的单例服务实例注入每个 DbContext 实例而引起的。例如,调用“UseLoggerFactory”每次都会传入一个新实例 - 有关更多详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=869049。这可能会导致性能问题,请考虑检查对“DbContextOptionsBuilder”的调用,这可能需要构建新的服务提供程序。通过将事件 ID“CoreEventId.ManyServiceProvidersCreatedWarning”传递给“DbContext.OnConfiguring”或“AddDbContext”中的“ConfigureWarnings”方法,可以抑制或记录此异常。

正如该错误所暗示的那样,我没有做任何奇怪的事情DbContextOptionsBuilder。我不知道如何诊断“......这可能需要建立新的服务提供商”。在大多数测试中,我通常创建一个上下文:new DbContextOptionsBuilder<TContext>().UseSqlite("DataSource=:memory:")其中TContext是我上面提到的上下文类型之一。

我已经阅读了存储库上的许多问题,并发现 EF 对各种事物进行了大量缓存,但关于该主题的文档不存在。建议是“找到导致这么多服务提供商被缓存的原因”,但我不知道要寻找什么。

有两种解决方法

  • builder.EnableServiceProviderCaching(false)这显然对性能非常不利
  • builder.ConfigureWarnings(x => x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning))忽略了这个问题

我假设“服务提供者”是指 EF 的内部 IoC 容器。

我想知道的是:我有很多DbContext类型(因此IModel类型)这一事实是否会影响服务提供者缓存?两者有关联吗?(我知道 EFIModel为每个缓存一个DbContext,它是否也为每个缓存一个服务提供者?)

Iva*_*oev 7

服务提供者缓存纯粹基于上下文选项配置 - 上下文类型、模型等并不重要。

在EF Core 5.0中,根据源码来看关键是

static long GetCacheKey(IDbContextOptions options) => options.Extensions
    .OrderBy(e => e.GetType().Name)
    .Aggregate(0L, (t, e) => (t * 397) ^ ((long)e.GetType().GetHashCode() * 397) ^ e.Info.GetServiceProviderHashCode());
Run Code Online (Sandbox Code Playgroud)

而在 EF Core 6.0 中,关键是重写的选项实例Equals具有相似语义的重写方法的选项实例。

因此,您使用的选项有所不同 - 无论是在最初还是在OnConfiguring调用后(如果您要覆盖它并修改其中的选项)。这就是您需要弄清楚的(在 5.0 中您可以使用上述方法来检查密钥,在 6.0 中您可以使用一些静态字段来存储第一个选项实例并使用它来检查下一个Equals选项)。

请注意,EF Core 会缓存原始选项和OnConfiguring调用后选项,因此它们都很重要。顺便说一句,生成警告的代码位于同一位置(类)- source

  • @IvanStoev 我只是想说谢谢。您的回答帮助我解决了同样的问题。就我而言,我提供了一个证书回调 (postgres),即使它是相同的证书,使用 lambda 进行回调也会导致缓存密钥发生更改。转移到包含单例证书的单例委托修复了它。再次感谢。 (2认同)