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.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With