Home > Net >  EF Core internal caching and many DbContext types during testing
EF Core internal caching and many DbContext types during testing

Time:12-28

I have many test classes, and each has dozens of tests. I want to isolate tests, so instead of a mega context MyDbContext, I use MyDbContextToTestFoo, MyDbContextToTestBar, MyDbContextToTestBaz, etc. So I have MANY DbContext subclasses.

In my unit tests with EF Core 5 I'm running into the ManyServiceProvidersCreatedWarning. They work individually, but many fail when run as a group:

System.InvalidOperationException : An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.ManyServiceProvidersCreatedWarning': More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. This is commonly caused by injection of a new singleton service instance into every DbContext instance. For example, calling 'UseLoggerFactory' passing in a new instance each time--see https://go.microsoft.com/fwlink/?linkid=869049 for more details. This may lead to performance issues, consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. This exception can be suppressed or logged by passing event ID 'CoreEventId.ManyServiceProvidersCreatedWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.

I don't do anything weird with DbContextOptionsBuilder as that error suggests. I don't know how to diagnose "...that may require new service providers to be built". In most tests I create a context normally: new DbContextOptionsBuilder<TContext>().UseSqlite("DataSource=:memory:") where TContext is one of the context types I mentioned above.

I've read many issues on the repo, and discovered that EF does heavy caching of all sorts of things, but docs on that topic don't exist. The recommendation is to "find what causes so many service providers to be cached", but I don't know what to look for.

There are two workarounds:

  • builder.EnableServiceProviderCaching(false) which is apparently very bad for perf
  • builder.ConfigureWarnings(x => x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning)) which ignores the problem

I assume that "service provider" means EF's internal IoC container.

What I want to know is: does the fact that I have many DbContext types (and thus IModel types), affect service provider caching? Are the two related? (I know EF caches an IModel for every DbContext, does it also cache a service provider for each one?)

CodePudding user response:

Service provider caching is purely based on the context options configuration - the context type, model etc. doesn't matter.

In EF Core 5.0, the key according to the source code is

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());

while in EF Core 6.0 the key is the options instance with overridden Equals method having similar semantics.

So something is different in the options you are using - either initially or after OnConfiguring call if you are overriding it and modifying the options inside. This is what you need to figure out (in 5.0 you could use the above method to check the key, in 6.0 you could use some static field for storing the first options instance and use it to check with Equals the next ones).

Note that EF Core caches both original and after OnConfiguring call options, so all they count. The code generating the warning btw is in the same place (class) - source.

  • Related