EF Core ManyServiceProvidersCreatedWarning Exception

Discussion of open issues, suggestions and bugs regarding ADO.NET provider for Oracle
Post Reply
roperp
Posts: 2
Joined: Tue 11 May 2021 08:20

EF Core ManyServiceProvidersCreatedWarning Exception

Post by roperp » 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:

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'.
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:

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;
}
Each DbContext looks like this:

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

Shalex
Site Admin
Posts: 9543
Joined: Thu 14 Aug 2008 12:44

Re: EF Core ManyServiceProvidersCreatedWarning Exception

Post by Shalex » Thu 13 May 2021 13:59

The description of the issue at viewtopic.php?t=40869 is up-to-date. We have not found a solution for the issue in the scope of current provider implementation.

roperp
Posts: 2
Joined: Tue 11 May 2021 08:20

Re: EF Core ManyServiceProvidersCreatedWarning Exception

Post by roperp » Wed 19 May 2021 08:32

Thank you for the update, as suggested in the other thread we're working around the issue using:

Code: Select all

optionsBuilder.ConfigureWarnings(x => x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning));
in our DbContexts for now and keeping an eye on memory usage

Post Reply