In our project we have a database where we use the DB first approach. Since we use features which do not get scaffolded I have a second DBContext which inherits from the generated one. This allows me to avoid manually manipulating the generated db context each time a database change occurs.
So the class definitions and usage in StartUp looks like:
// the db context generated by scaffolding the database
public partial class ApplicationDatabaseContextGenerated : DbContext
{
public ApplicationDatabaseContextGenerated() {}
public ApplicationDatabaseContextGenerated(DbContextOptions<ApplicationDatabaseContextGenerated> options) : base(options) {}
// the db sets scaffolded
}
// the db context used by the app with the extended functionality
public class ApplicationDatabaseContext : ApplicationDatabaseContextGenerated
{
public ILogger<ApplicationDatabaseContext> Logger { get; protected set; }
public ApplicationDatabaseContext() : base() {}
public ApplicationDatabaseContext(DbContextOptions<ApplicationDatabaseContext> options, ILogger<ApplicationDatabaseContext> logger) : base(options)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
// the extended functionality like views and functions
}
// how I use it in startup
public void ConfigureServices(IServiceCollection services)
{
// other things ...
services.AddDbContext<ApplicationDatabaseContext>(options => options.UseNpgsql(Configuration.GetConnectionString("db1-connection")));
// other things ...
}
This unfortunatly results in a compilation error higlighting the base(options) of the ApplicationDatabaseContext constructor stating:
Error CS1503 Argument 1: cannot convert from 'Microsoft.EntityFrameworkCore.DbContextOptions<... ApplicationDatabaseContext>' to 'Microsoft.EntityFrameworkCore.DbContextOptions<... ApplicationDatabaseContextGenerated>'
I thought, let's be smart, a ApplicationDatabaseContextGenerated is basically a database context and changed the constructor of the ApplicationDatabaseContextGenerated to:
public ApplicationDatabaseContextGenerated(DbContextOptions<DbContext> options) : base(options) {}
Nope, that's not allowed neither and results in:
Error CS1503 Argument 1: cannot convert from 'Microsoft.EntityFrameworkCore.DbContextOptions<... ApplicationDatabaseContext>' to 'Microsoft.EntityFrameworkCore.DbContextOptions<Microsoft.EntityFrameworkCore.DbContext>'
Ok, let' take both as DbContext
// the db context generated by scaffolding the database
public partial class ApplicationDatabaseContextGenerated : DbContext
{
public ApplicationDatabaseContextGenerated() {}
public ApplicationDatabaseContextGenerated(DbContextOptions<DbContext> options) : base(options) {}
// the db sets scaffolded
}
// the db context used by the app with the extended functionality
public class ApplicationDatabaseContext : ApplicationDatabaseContextGenerated
{
public ILogger<ApplicationDatabaseContext> Logger { get; protected set; }
public ApplicationDatabaseContext() : base() {}
public ApplicationDatabaseContext(DbContextOptions<DbContext> options, ILogger<ApplicationDatabaseContext> logger) : base(options)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
// the extended functionality like views and functions
}
Pretty, that compiles. Let's start it and we get:
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<TContext> object in its constructor and passes it to the base constructor for DbContext.
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_Model()
at Microsoft.EntityFrameworkCore.Internal.InternalDbQuery`1.get_EntityType()
at Microsoft.EntityFrameworkCore.Internal.InternalDbQuery`1.get_EntityQueryable()
at Microsoft.EntityFrameworkCore.Internal.InternalDbQuery`1.System.Linq.IQueryable.get_Provider()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.SingleAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at ... MyMethod(ApplicationDatabaseContext dbContext, CancellationToken cancellationToken) in myfile.cs:line 51
at ... MyOtherMethod in myfile.cs:line 61
Well, how to convert DbContextOptions or how to inherit DbContexts?
DbContextOptions<TContext>
is a generic class, and as any class it doesn't support covariance, hence DbContextOptions<TDerivedContext>
cannot be treated as DbContextOptions<TBaseContext>
.
The solution is to use the non generic DbContextOptions
class in the base context constructor (similar to the DbContext
class constructor with options):
public ApplicationDatabaseContextGenerated(DbContextOptions options) : base(options) { }
Since the AddDbContext<ApplicationDatabaseContext>
will create an instance of the DbContextOptions<ApplicationDatabaseContext>
class, the ApplicationDatabaseContext
class constructor can use either DbContextOptions<ApplicationDatabaseContext>
or DbContextOptions
options parameter.
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