How and where to call Database.EnsureCreated and Database.Migrate?
Solution 1
I think this is an important question and should be well answered!
What is Database.EnsureCreated?
context.Database.EnsureCreated()
is new EF core method which ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created and also it ensures it is compatible with the model for this context.
Note:
This method does not use migrations to create the database. In addition, the database that is created cannot later be updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate()
method to ensure the database is created and all migrations are applied.
How did we do that with EF 6?
context.Database.EnsureCreated()
is equivalent to the below listed approaches of EF 6:
-
Package Manager Console:
Enable-Migrations -EnableAutomaticMigrations. Add-Migration/Update-Database.
-
From code:
Database.SetInitializer CreateDatabaseIfNotExists
or
With DbMigrationsConfiguration and set AutomaticMigrationsEnabled = true;
What is Database.Migrate?
Applies any pending migrations for the context to the database. Will create the database if it does not already exist.
How did we do that with EF 6?
context.Database.Migrate()
is equivalent to the below listed approaches of EF 6:
-
Package Manager Console:
Update-Database -TargetMigration
-
With a custom DbMigrationsConfiguration:
AutomaticMigrationsEnabled = false; or with DbMigrator.
Conclusion:
If you are using migrations there is context.Database.Migrate()
. If you don't want migrations and just want a quick database (usually for testing) then use context.Database.EnsureCreated()/EnsureDeleted().
Solution 2
With the information that James P and Bassam Alugili provided, what I ended up doing was to add these lines of code to the Configure
method in the Startup
class (Startup.cs):
using (var scope =
app.ApplicationServices.CreateScope())
using (var context = scope.ServiceProvider.GetService<MyDbContext>())
context.Database.Migrate();
Solution 3
Ordinarily, the DbContext
will be added to the dependency injection container in Startup.ConfigureServices()
like so:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add DbContext to the injection container
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
this.Configuration.GetConnectionString("DefaultConnection")));
}
}
However, the IServiceCollection
doesn't act as a service provider, and since the DbContext
was not registered with the injection container before the current scope (Startup.ConfigureServices
), we can't access the context through dependency injection here.
Henk Mollema discusses manually resolving services during startup here, but mentions that...
manually resolving services (aka Service Locator) is generally considered an anti-pattern ... [and] you should avoid it as much as possible.
Henk also mentions that the Startup
constructor's dependency injection is very limited and does not include services configured in Startup.ConfigureServices()
, so DbContext usage is easiest and most appropriate through the injection container used throughout the rest of the app.
The runtime's hosting service provider can inject certain services into the constructor of the
Startup
class, such asIConfiguration
,IWebHostEnvironment
(IHostingEnvironment
in pre-3.0 versions),ILoggerFactory
andIServiceProvider
. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.
In order to call Database.EnsureCreated()
or Database.Migrate()
, we can, and want to, have the DbContext resolve automatically in Startup.Configure()
, where our configured services are now available through DI:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add DbContext to the injection container
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
this.Configuration.GetConnectionString("DefaultConnection")));
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
{
if (env.IsDevelopment())
{
context.Database.EnsureCreated();
//context.Database.Migrate();
}
}
}
Please remember as Bassam Alugili's answer referenced from EF Core documentation that Database.EnsureCreated()
and Database.Migrate()
are not meant to be used together because one ensures your existing migrations are applied to the database, which is created if needed. The other just ensures a database exists, and if not, creates one that reflects your DbContext
, including any seeding done through the Fluent API in the context.
Solution 4
Just as a foreward you should read this from Rowan Miller:
...
EnsureCreated
totally bypasses migrations and just creates the schema for you, you can't mix this with migrations.EnsureCreated
is designed for testing or rapid prototyping where you are ok with dropping and re-creating the database each time. If you are using migrations and want to have them automatically applied on app start, then you can usecontext.Database.Migrate()
instead.
According to answer here you need to add Globals.EnsureDatabaseCreated();
it to Startup.cs
:
Startup function in Startup.cs:
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
Globals.Configuration = Configuration;
Globals.HostingEnvironment = env;
Globals.EnsureDatabaseCreated();
}
And define Globals.EnsureDatabaseCreated()
as follows:
public static void EnsureDatabaseCreated()
{
var optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:DataContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:DataContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:DataContext"]);
var context = new ApplicationContext(optionsBuilder.Options);
context.Database.EnsureCreated();
optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:TransientContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:TransientContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:TransientContext"]);
new TransientContext(optionsBuilder.Options).Database.EnsureCreated();
}
To use context.Database.Migrate()
see here or here.
Solution 5
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<YourDbContext>(option => option.UseSqlServer(@"Data source=(localdb)\ProjectModels;Initial Catalog=YourDb;Integrated Security=True"));
var app = builder.Build();
// Configure the HTTP request pipeline.
YourDbContext dbcontext = app.Services.GetRequiredService<YourDbContext>();
dbcontext.Database.EnsureCreated();
Related videos on Youtube
bailando bailando
Updated on July 08, 2022Comments
-
bailando bailando almost 2 years
I have a ASP.NET MVC 6 application, and i need to call the
Database.EnsureCreated
andDatabase.Migrate
methods.But where should I call them?
-
John Pankowicz about 6 yearsYou may not want to use either. MS docs say this about using Migrate(): "While it's great for apps with a local database, most applications will require more robust deployment strategy like generating SQL scripts." docs.microsoft.com/en-us/ef/core/managing-schemas/migrations
-
-
bailando bailando almost 8 yearsHello James, thank you for your answer!, i don't have any access to a vairable name Globals in my startup method, how can i get an access to it?
-
bailando bailando almost 8 yearsHello Bassam Alugili, thank you for your answer! in my project, i am using migrations, i didn't knew that one shouldn't use both of the methods together.
-
Bassam Alugili almost 8 yearsuw and here is an example how to call it! stefanhendriks.com/2016/04/29/…
-
Afshar Mohebi over 6 yearsI was thinking that
Database.Migrate()
creats migrations (if needed) then updates the based base on it. Just similar to automatic migration in EF 6. But I was wrong. It only applies existing migrations (if any) on the database. -
Ásgeir Gunnar Stefánsson over 6 yearsAs I understand Database.Migrate uses the same db creditials that are used by the app when doing insert/queries and etc agains the DB. Do we want these actions to be done by a user with create/drop privilages ? This there a way to let Database.Migrate() use other creditials (with create/drop privilages) ?
-
Shaswat Rungta over 6 yearsYou just saved me from a future disaster. Kudos
-
Douglas Gaskell over 5 yearsSame, not seeing a
Globals
. This looks like a non-standard way of trying to crowbar this in -
Josh Sutterfield almost 5 yearsFrom what I understand the standard way is the DbContext is created in Startup.ConfigureServices, but via kind of indirect methods. You can fish it back out there, or in Startup.Configure with app.ApplicationServices.GetRequiredService<T>. I think.
-
nivs1978 almost 5 yearsThis is exactly what I was looking for. Most examples use either .Net core or Web and I was in a Windows Forms application with .Net 4.6. Database was already created (because the user in the connection string has no rights to create databases). And the above code created all the tables and everything from the migrations.
-
Shimmy Weitzhandler almost 5 yearsWhere in the app's entry point would be good to call
dbcontext.Migrate
? -
Tom almost 5 yearsDoes
context.Database.Migrate()
execute each migration one after another, if the database does not exist? Other ORM like Rails ActiveRecord have commands that create the database from the current schemarails db:setup
instead ofrails db:create && rails db:migrate
.context.Database.EnsureCreated()
seems to work that way, but you say it is meant for testing? If the user installs version 5 of my app from scratch, he shouldn't go through the history of migrations I think. -
Tom over 4 yearsWhile this answer is correct and apparently quite helpful, I'm a little bit troubled to read the comments from people who seem surprised and/or grateful for correct information here. This is literally pasted from the very easily viewed online documentation. If you use this or any function, you really should read about it. It's literally one keystroke (F1) away. ...millenial snowflakes
-
Piotr M almost 3 yearsMost elegant way I have found to seed in memory db, thank you
-
Hossein over 2 yearsExcuseme, this is the link of my question that I think it is related to this case: stackoverflow.com/questions/70512142/… . Would you take a look at it and see if your sulotion is helpful for me or not? if not, can you give me some sulotions to solve the issue?
-
denisv over 2 yearsThat's a very clever way to seed in-memory database. Thanks.
-
moccajoghurt about 2 yearsIn .NET 6 Startup.cs no longer exists. Do we have an update on where to use it now?
-
Manish about 2 yearsThanks, this was a lifesaver for aspnetcore6. In aspnetcore5, this was within a scope. I hope this also takes case of dependency injection in the aspnetcore6 web app.
-
Yeronimo about 2 years@moccajoghurt You can use it where var app = CreateHostBuilder(args).Build(); is and then something like: using (var scope = app.Services.CreateScope()) using (var context = scope.ServiceProvider.GetService<MyContext>()) context.Database.Migrate();
-
moccajoghurt about 2 years@Yeronimo var app = CreateHostBuilder(args).Build(); also no longer exists.
-
Yeronimo about 2 yearsNet 6 var builder = WebApplication.CreateBuilder(args); var app = builder.Buil(); using (var scope = app.Services.CreateScope()) { using (var context = scope.ServiceProvider.GetService<VindDbContext>()) { context.Database.Migrate(); } }
-
XWIKO about 2 yearsIm running .NET 6, and am getting "Cannot resolve scoped service 'CosmosTest.EfCore.CosmosContext' from root provider"