How and where to call Database.EnsureCreated and Database.Migrate?
Asked Answered
B

9

160

I have a ASP.NET Core 6 MVC application, and I need to call the Database.EnsureCreated and Database.Migrate methods.

But where should I call them?

Baruch answered 7/7, 2016 at 5:24 Comment(2)
You 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." learn.microsoft.com/en-us/ef/core/managing-schemas/migrationsEanes
This link may be useful to you: Apply migrations at runtimeAnadem
P
181

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:

  1. Package Manager Console:

    Enable-Migrations -EnableAutomaticMigrations. Add-Migration/Update-Database.

  2. 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:

  1. Package Manager Console:

    Update-Database -TargetMigration

  2. 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().

Penetrate answered 7/7, 2016 at 9:26 Comment(9)
Hello 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.Baruch
uw and here is an example how to call it! stefanhendriks.com/2016/04/29/…Penetrate
I 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.Mcdevitt
As 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) ?Roebuck
You just saved me from a future disaster. KudosKoto
Where in the app's entry point would be good to call dbcontext.Migrate?Hightoned
Does 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 schema rails db:setup instead of rails 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.Lavatory
Thanks for explaining the nuances of this feature. Other than testing, I dont see the value in ever using "EnsureCreated". In fact, it seems dangerous. I can imagine what would happen if this snuck into production code and a large number of databases and tables got built by accident. Not very good to stick such power into one line of code.Amberlyamberoid
What question is this answering exactly? This doesn't even address the question at all. The question is, "I have a web app, where do I call these methods?" not "What are these methods?"Bellhop
B
50

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();
Baruch answered 8/7, 2016 at 9:51 Comment(7)
This 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.Candycandyce
Excuseme, this is the link of my question that I think it is related to this case: #70512642 . 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?Comparable
In .NET 6 Startup.cs no longer exists. Do we have an update on where to use it now?Motley
@Motley 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();Buroker
@Buroker var app = CreateHostBuilder(args).Build(); also no longer exists.Motley
Net 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(); } }Buroker
I used the above snippet in the integration tests to generate the database using entity framework with a mix of code first and database scripts. Note: Ensure Created failed with an error in the script, though the Migrate method works like charm.Lyublin
Y
27

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 as IConfiguration, IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. 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.

Yoruba answered 25/2, 2020 at 17:9 Comment(2)
Most elegant way I have found to seed in memory db, thank youPrivet
That's a very clever way to seed in-memory database. Thanks.Calzada
R
19

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 use context.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.

Recountal answered 7/7, 2016 at 8:11 Comment(3)
Hello 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?Baruch
Same, not seeing a Globals. This looks like a non-standard way of trying to crowbar this inCrookback
From 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.Sharkey
C
12
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();
Capitalism answered 24/2, 2022 at 21:14 Comment(3)
Thanks, 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.Jaimeejaimes
Im running .NET 6, and am getting "Cannot resolve scoped service 'CosmosTest.EfCore.CosmosContext' from root provider"Code
@Code i get into the same issue, you have to create a scope first (using var scope = app.Services.CreateScope()), then get the required service through it (var db = scope.ServiceProvider.GetRequiredService<MyDbContext>()): sourceSetter
M
8

Constructor

public class AppDbContext : DbContext
{
    // Properties with entities of 
    // public DbSet<User> Users { get; set; }
    // public DbSet<Cart> Carts { get; set; }
    // public DbSet<CartItem> CartItems { get; set; }
    // public DbSet<Product> Products { get; set; }
    // ...
    
    public AppDbContext()
    {
        Database.Migrate();
        // Or Database.EnsureCreated();
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Model configuration
        // modelBuilder.Entity<CartItem>().HasKey(x => new { x.CartId, x.ProductId })

        base.OnModelCreating(modelBuilder);
    }
}

Pros

  • It is the simplest place to call Database.Migrate or Database.EnsureCreated.
  • The methods are guaranteed to be called before you start to use AppDbContext.

Cons

  • You cannot use the async variants of the methods. It is a big problem in environments where using of thread-blocking operations is not acceptable. (e.g unit tests or UI threads).
  • The methods are called on every AppDbContext instantiation.
  • If something is wrong with a database model, a connection string or even DBMS, you will find out it only on an instantiation. In case of ASP.NET Core it can be the first call to an endpoint that depends on AppDbContext.

Startup

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Service configurations

var app = builder.Build();
try 
{
    using (var serviceScope = app.Services.CreateScope())
    {
        var dbContext = serviceScope.ServiceProvider.GetRequiredService<AppDbContext>();
        await dbContext.Database.MigrateAsync();
        // or dbContext.Database.EnsureCreatedAsync();
    }
    
    // Middleware pipeline configuration

    app.Run();
} 
catch (Exception e) 
{
    app.Logger.LogCritical(e, "An exception occurred during the service startup");
}
finally
{
    // Flush logs or else you lose very important exception logs.
    // if you use Serilog you can do it via
    // await Log.CloseAndFlushAsync();
}

Pros

  • The methods are guaranteed to be called before an application starts.
  • The method are called once.
  • You can use the async varians.
  • If something is wrong you will find out it before an application starts.

Cons

  • If you have a large migration that takes a lot of time to be applied, an application can exceed statup timeouts that can be used in an environment where you run the application.

    If it happens you can temporarily increase the timeout to apply migrations and revert it to the default value.


IHostedService

// InitializationService.cs
public sealed class InitializationService : IHostedService
{
    #region Constructor and dependencies

    private readonly IServiceProvider _serviceProvider;
    private readonly Options _options;

    public InitializationService(IServiceProvider serviceProvider, IOptions<Options> options)
    {
        _serviceProvider = serviceProvider;
        _options = options.Value;
    }

    #endregion

    public async Task StartAsync(
        // Use this token to detect the application stopping
        CancellationToken cancellationToken
    )
    {
        using var serviceScope = _serviceProvider.CreateScope();
        var serviceProvider = serviceScope.ServiceProvider;

        if (!_options.SkipMigration)
        {
            var context = serviceProvider.GetRequiredService<AppDbContext>();

            await context.Database.MigrateAsync(cancellationToken);
        }

        // ... Other initialization logic of the application. (e.g. a seeding of an initial data)
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

    public sealed class Options
    {
        public const string Position = "Initialization";

        public bool SkipMigration { get; set; }

        // ... Other options for initialization service
    }
}

public static class InitializationServiceExtensions
{
    public static void AddInitializationService(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddHostedService<InitializationService>();

        serviceCollection
            .AddOptions<InitializationService.Options>()
            .BindConfiguration(InitializationService.Options.Position);
    }
}

Pros

  • The methods are guaranteed to be called before an application starts.
  • The method are called once.
  • You can use the async varians.
  • If something is wrong you will find out it before an application starts.
  • You can gracefully shutdown an application even during the initialization.

Cons

  • You cannot use a database right after var app = builder.Build();.

    I faced this issue when I tried to use Hangfire dashboard and shared a connection string between Hangfire and EF Core. Hangfire dashboard need at least that a database is created.

    I've solved this problem by following code:

    /// <remarks>
    /// It cannot be done in <see cref="InitializationService"/>
    /// because Hangfire requires that db already has been created
    /// before Hangfire dashboard is configured
    /// </remarks>
    public static async Task CreateEmptyDbIfNotExists(WebApplication app)
    {
        using var serviceScope = app.Services.CreateScope();
        var serviceProvider = serviceScope.ServiceProvider;
    
        var skipMigration = serviceProvider
            .GetRequiredService<IOptions<InitializationService.Options>>()
            .Value.SkipMigration;
    
        if (skipMigration)
            return;
    
        var context = serviceProvider.GetRequiredService<AppDbContext>();
    
        var databaseCreator = context.GetService<IDatabaseCreator>() as RelationalDatabaseCreator;
        if (!await databaseCreator.ExistsAsync())
            await databaseCreator.CreateAsync();
    }
    
Musteline answered 2/7, 2023 at 7:39 Comment(0)
T
1

Additionally you may see a performance hit if you call this in the constructor of your context... After moving EnsureCreated to the setup.cs utility, I noticed considerable improvements to my response times.

Note: I am using EFC and UWP.

Tehuantepec answered 24/4, 2018 at 15:30 Comment(0)
W
-1

If you are working with VS 2022 / .Net Version 6 and you are trying to find a way to create your database..then

Do these following steps

Add Microsoft.EntityFramework.Tools reference through Package manager from Package Manager Console

Run Step 1

Add-Migration InitialMigration

InitialMigration here is custom name you can type anything you want.. let it run

Step 2

Update-Database

This should create your database.

Wonderful answered 13/8, 2022 at 21:55 Comment(1)
this does not create the database. this is for migrationKavita
P
-1

I am developing ASP .NET Core 6 Web API.

In Program.cs after building the app I wrote the code below.

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    using var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    context.Database.EnsureCreated();
}

context.Database.EnsureCreated() ensures that the database for the context exists.

  • If the database exists and has any tables, then no action is taken. Nothing is done to ensure the database schema is compatible with the Entity Framework model.
  • If the database exists but does not have any tables, then the Entity Framework model is used to create the database schema.
  • If the database does not exist, then the database is created and the Entity Framework model is used to create the database schema.

I hope I helped.

Posthorse answered 15/2, 2023 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.