Search code examples
entity-framework-coreef-core-2.0ef-core-2.2ef-core-2.1

More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework


I am getting this warning in my ASP.NET Core 2.2 application

warn: Microsoft.EntityFrameworkCore.Infrastructure[10402] 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.

After spending some time i figured out its happening in startup.cs. I am using IdentityServer3 + OpenIDCnnection for authetication.

After user successfully logs-in, The client application authorizes the user making sure user exists in the client application's database.

Startup.cs of Client Application

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

Account Service

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) will get invoked for every successful login. And so on 21st successful user and onward i start seeing warning.

Questions

1>In OnTokenValidated event I am creating service provider and immediately disposing it. Why EF is still logging warning?
2>How do i get rid of this warning?

I get this warning even if i create 20+ DBContext using scope

NET Fiddle DEMO


Solution

  • Solved the issue by unnecessary building service provider. The context parameter has HttpContext. And HttpContext provide access to ServiceProvider

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