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.
DbContext
itself and any dependencies in its constructor need to be registered as services in the application's service provider.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 without 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;
DbContext
constructor takes additional parameters that are not registered in DIBuildWebHost
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.
optionsBuilder
.UseSqlServer(connectionString, providerOptions=>providerOptions.CommandTimeout(60))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
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:DbContextOptions
can be supplied to the DbContext
by overriding the OnConfiguring
method or externally via a constructor argument.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.
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.
You can add the DbContext
to dependency injection as shown below.
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 the 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>>();