Rob Rob - 9 days ago 5
C# Question

UseInMemoryDatabase with UseInternalServiceProvider. No database provider configured

I'm having trouble injecting a custom

IAsyncQueryProvider
when using EntityFrameworkCore. To be more precise.. I am having trouble injecting the provider when using the in memory database functionality provided. Using a default provider (SqlServer), all works fine.

Here's my global
Startup.cs


private void ConfigureEntityFrameworkWithSecurity(IServiceCollection services)
{
services
.AddEntityFramework()
.AddEntityFrameworkSqlServer()
.AddScoped<IAsyncQueryProvider, CustomEntityProvider>()
.AddDbContext<APIContext>((sp, options) =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
.UseInternalServiceProvider(sp);
});
}


This works flawlessly, and I can put a breakpoint within
CustomEntityProvider
to verify that it is indeed being injected. At the moment,
CustomEntityProvider
simply implements
IAsyncQueryProvider
, and simply passes through the request. There is no logic contained within it.

When I'm running a test, I configure the webhost to use a different
Startup
file:

public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env) : base(env)
{
}

public override void ConfigureServices(IServiceCollection services)
{
services
.AddDbContext<APIContext>((sp, options) =>
{
options.UseInMemoryDatabase()
.UseInternalServiceProvider(sp);
});
base.ConfigureServices(services);
}
}


Running a test with
TestStartup
yields the error:


System.InvalidOperationException : No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.


And
APIContext
is correctly defined:

public class APIContext : DbContext
{
public APIContext(DbContextOptions<APIContext> options)
: base(options)
{
}
...
}


Removing
UseInternalServiceProvider
from
TestStartup
works correctly - however, I don't want my tests to hit an actual database. Further, I would expect
UseInMemoryDatabase
to automatically inject dependencies into the service provider - as it works perfectly fine by itself.

The error is confusing because the in memory database is the provider I want to use.

Rob Rob
Answer

Unfortunately, the solution is hair-tearingly simple. However, there seems to be very little documentation about using dependency injection with the in-memory database functionality. It appears to be one or the other. Hopefully this question will provide help for future people misfortunate enough to run into this.

I downloaded the EntityFramework source to investigate, and found that calling UseInMemoryDatabase creates an extension InMemoryOptionsExtension which itself will add to the service provider, namely:

public virtual void ApplyServices(IServiceCollection services)
{
    Check.NotNull(services, nameof(services));

    services.AddEntityFrameworkInMemoryDatabase();
}

And the solution is as simple as it looks:

public class TestStartup : Startup
{
    public TestStartup(IHostingEnvironment env) : base(env)
    {
    }

    public override void ConfigureServices(IServiceCollection services)
    {
        services
            .AddEntityFrameworkInMemoryDatabase()
            .AddDbContext<APIContext>((sp, options) =>
            {
                options.UseInMemoryDatabase().UseInternalServiceProvider(sp);
            });
        base.ConfigureServices(services);
    }
}