This article shows basic patterns for configuring a DbContext via a DbContextOptions to connect to a database using a specific EF Core provider and optional behaviors.
Some of the EF Core Tools commands such as the Migrations commands require a derived DbContext
instance to be created at design time to gather details about the application's entity types and how they map to a database schema. In most cases, it is desirable that the DbContext thereby created is configured in a similar way to how it would be configured at run time.
There are various ways the tools try to create the DbContext:
If your startup project uses the ASP.NET Core Web Host or .NET Core Generic Host, the tools try to obtain the DbContext
object from the application's service provider.
The tools first try to obtain the service provider by invoking Program.CreateHostBuilder()
, calling Build()
, then accessing the Services
property.
public class Program
{
public static void Main(string[] args)
=> CreateHostBuilder(args).Build().Run();
// EF Core uses this method at design time to access the DbContext
public static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(
webBuilder => webBuilder.UseStartup<Startup>());
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
=> services.AddDbContext<EntityContext>();
}
public class EntityContext : DbContext
{
public EntityContext(DbContextOptions<EntityContext> options)
: base(options)
{
}
}
When you create a new ASP.NET Core application, this hook is included by default. The DbContext
itself and any dependencies in its constructor need to be registered as services in the application's service provider. This can be easily achieved by having a constructor on the DbContext
that takes an instance of DbContextOptions<TContext>
as an argument and using the AddDbContext<TContext>
method.
If the DbContext
can't be obtained from the application service provider, the tools look for the derived DbContext
type inside the project. Then they try to create an instance using a constructor with no parameters. This can be the default constructor if the DbContext
is configured using the OnConfiguring
method.
You can also tell the tools how to create your DbContext
by implementing the IDesignTimeDbContextFactory<TContext>
interface. If a class implementing this interface is found in either the same project as the derived DbContext
or in the application's startup project, the tools bypass the other ways of creating the DbContext
and use the design-time factory instead.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class EntityContextFactory : IDesignTimeDbContextFactory<EntityContext>
{
public EntityContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<EntityContext>();
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\ProjectsV13;Initial Catalog=NewBookStore;Trusted_Connection=True;MultipleActiveResultSets=true");
return new EntityContext(optionsBuilder.Options);
}
}
}
Before EFCore 5.0, the args
parameter was unused and it is fixed in EFCore 5.0 and any additional design-time arguments are passed into the application through that parameter.
A design-time factory can be especially useful if you need to configure the DbContext
differently for design time than at run time, if the DbContext
constructor takes additional parameters are not registered in DI, if you are not using DI at all, or if for some reason you prefer not to have a BuildWebHost
method in your ASP.NET Core application's Main
class.
The DbContext
must have an instance of DbContextOptions
to perform any work. The DbContextOptions
instance carries configuration information such as:
UseSqlServer
or UseSqlite
etc. These extension methods require the corresponding provider package, such as Microsoft.EntityFrameworkCore.SqlServer
or Microsoft.EntityFrameworkCore.Sqlite
. The methods are defined in the Microsoft.EntityFrameworkCore
namespace.The following example configures the DbContextOptions to use the SQL Server provider, a connection contained in the connectionString variable, a provider-level command timeout, and an EF Core behavior selector that makes all queries executed in the DbContext no-tracking by default:
optionsBuilder
.UseSqlServer(connectionString, providerOptions=>providerOptions.CommandTimeout(60))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
The DbContextOptions
can be supplied to the DbContext
by overriding the OnConfiguring
method or externally via a constructor argument.
If both are used, OnConfiguring
is applied last and can overwrite options supplied to the constructor argument.
Your constructor can simply accept a DbContextOptions
as follows:
public class EntityContext : DbContext
{
public EntityContext(DbContextOptions<EntityContext> options)
: base(options)
{ }
public DbSet<Author> Authors { get; set; }
}
The base constructor of DbContext
also accepts the non-generic version of DbContextOptions
, but using the non-generic version is not recommended for applications with multiple context types.
Your application can now pass the DbContextOptions
when instantiating a context, as follows:
var optionsBuilder = new DbContextOptionsBuilder<EntityContext>();
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\ProjectsV13;Initial Catalog=NewBookStore;Trusted_Connection=True;MultipleActiveResultSets=true");
using (var context = new EntityContext(optionsBuilder.Options))
{
// code here
}
You can also initialize the DbContextOptions
within the context itself. While you can use this technique for basic configuration, you will typically still need to get certain configuration details from the outside, e.g. a database connection string. This can be done with a configuration API or any other means.
To initialize DbContextOptions
within the context, override the OnConfiguring
method and call the methods on the provided DbContextOptionsBuilder
:
public class EntityContext : DbContext
{
public DbSet<Author> Authors { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\ProjectsV13;Initial Catalog=NewBookStore;Trusted_Connection=True;MultipleActiveResultSets=true");
}
}
An application can simply instantiate such a context without passing anything to its constructor:
using (var context = new EntityContext())
{
// code here
}
EF Core supports using DbContext
with a dependency injection container. Your DbContext
type can be added to the service container by using the AddDbContext<TContext>
method.
AddDbContext<TContext>
will make both your DbContext
type, TContext
, and the corresponding DbContextOptions<TContext>
available for injection from the service container.
Add the DbContext
to dependency injection.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<EntityContext>(options => options.UseSqlServer(ConnectionString));
}
This requires adding a constructor argument to your DbContext
type that accepts DbContextOptions<TContext>
.
public class EntityContext : DbContext
{
public EntityContext(DbContextOptions<EntityContext> options)
:base(options)
{ }
public DbSet<Author> Authors { get; set; }
}
Here is the code used in ASP.NET Core application:
public class AuthorController
{
private readonly EntityContext _context;
public AuthorController(EntityContext context)
{
_context = context;
}
...
}
Here is the code using ServiceProvider directly which is less common:
using (var context = serviceProvider.GetService<EntityContext>())
{
// code here
}
var options = serviceProvider.GetService<DbContextOptions<EntityContext>>();