How and where to call Database.EnsureCreated and Database.Migrate?

asp.netasp.net MvcEntity FrameworkEntity Framework-Core

asp.net Problem Overview


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

But where should I call them?

asp.net Solutions


Solution 1 - asp.net

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.

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

Solution 2 - asp.net

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 - asp.net

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.

Solution 4 - asp.net

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.

Solution 5 - asp.net

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();

Solution 6 - asp.net

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionbailando bailandoView Question on Stackoverflow
Solution 1 - asp.netBassam AlugiliView Answer on Stackoverflow
Solution 2 - asp.netbailando bailandoView Answer on Stackoverflow
Solution 3 - asp.netJames PView Answer on Stackoverflow
Solution 4 - asp.netKyllian MobleyView Answer on Stackoverflow
Solution 5 - asp.netindi.ńêò WijeweeraView Answer on Stackoverflow
Solution 6 - asp.netviscView Answer on Stackoverflow