EF Core ManyServiceProvidersCreatedWarning Exception
Posted: Tue 11 May 2021 09:51
Hello!
We are currently using EF Core v3.1.8 with Devart.Data.Oracle.EFCore v9.13.1098 in a multi-tenant environment and are hitting an EF Core exception in our ASP.NET Core 3.1 APIs. The exception is triggered for all requests that need a DbContext using a new connection string after 20 DbContexts with different connection strings have been used.
The exception is:
I've created a standalone ASP.NET Core 3.1 app using the same EF Core / Devart.Data.Oracle.EFCore versions as our APIs and can reproduce the issue by creating 20 DbContexts with 20 different connection strings and then using them to fetch data like this:
Each DbContext looks like this:
The exception is thrown on "dbContext20.Contact.FirstOrDefault();". Commenting out that line allows the request to complete successfully. The request also completes successfully when calling the same DbContext 20 times.
After reading through the EF Core code, I noticed that the ServiceProviderCache used by EF Core generates a cache key from the DbOptions extensions:
https://github.com/dotnet/efcore/blob/v ... cs#L70-L72
Debugging with the EF Core symbols shows that a different cache key is being generated for each DbContext which causes a new ServiceProvider to be created. It looks like the Devart OracleOptionsExtension contains the connection string which I suppose could change the hash code generated here. I don't know how e.Info.GetServiceProviderHashCode is implemented for the OracleOptionsExtension though so this is just a guess!
We've tried ignoring the warning as suggested in this thread:
viewtopic.php?t=40869#p172601
This prevents the exception being thrown but won't stop new ServiceProviders being created by EF for each DbContext that uses a new connection string.
Is a fix possible for this so that EF Core can re-use ServiceProviders with this setup or is this intended behaviour in Devart.Data.Oracle.EFCore? Our main concern is that the ServiceProviderCache will keep growing with the number of the tenants using our APIs and so could cause some memory usage problems in the future
We are currently using EF Core v3.1.8 with Devart.Data.Oracle.EFCore v9.13.1098 in a multi-tenant environment and are hitting an EF Core exception in our ASP.NET Core 3.1 APIs. The exception is triggered for all requests that need a DbContext using a new connection string after 20 DbContexts with different connection strings have been used.
The exception is:
Code: Select all
System.InvalidOperationException: Error 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. 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'.
Code: Select all
[HttpGet]
public async Task<bool> GetAsync()
{
dbContext1.Contact.FirstOrDefault();
dbContext2.Contact.FirstOrDefault();
dbContext3.Contact.FirstOrDefault();
dbContext4.Contact.FirstOrDefault();
dbContext5.Contact.FirstOrDefault();
dbContext6.Contact.FirstOrDefault();
dbContext7.Contact.FirstOrDefault();
dbContext8.Contact.FirstOrDefault();
dbContext9.Contact.FirstOrDefault();
dbContext10.Contact.FirstOrDefault();
dbContext11.Contact.FirstOrDefault();
dbContext12.Contact.FirstOrDefault();
dbContext13.Contact.FirstOrDefault();
dbContext14.Contact.FirstOrDefault();
dbContext15.Contact.FirstOrDefault();
dbContext16.Contact.FirstOrDefault();
dbContext17.Contact.FirstOrDefault();
dbContext18.Contact.FirstOrDefault();
dbContext19.Contact.FirstOrDefault();
dbContext20.Contact.FirstOrDefault();
return true;
}
Code: Select all
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace ServiceProviderExceptionFinder
{
public class DbContext1 : DbContext
{
public DbSet<Contact> Contact { get; set; }
private readonly ILoggerFactory _loggerFactory;
public DbContext1(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseLoggerFactory(_loggerFactory)
.UseOracle("<CONNECTION_STRING>");
}
}
}
After reading through the EF Core code, I noticed that the ServiceProviderCache used by EF Core generates a cache key from the DbOptions extensions:
https://github.com/dotnet/efcore/blob/v ... cs#L70-L72
Debugging with the EF Core symbols shows that a different cache key is being generated for each DbContext which causes a new ServiceProvider to be created. It looks like the Devart OracleOptionsExtension contains the connection string which I suppose could change the hash code generated here. I don't know how e.Info.GetServiceProviderHashCode is implemented for the OracleOptionsExtension though so this is just a guess!
We've tried ignoring the warning as suggested in this thread:
viewtopic.php?t=40869#p172601
This prevents the exception being thrown but won't stop new ServiceProviders being created by EF for each DbContext that uses a new connection string.
Is a fix possible for this so that EF Core can re-use ServiceProviders with this setup or is this intended behaviour in Devart.Data.Oracle.EFCore? Our main concern is that the ServiceProviderCache will keep growing with the number of the tenants using our APIs and so could cause some memory usage problems in the future