Reputation: 6375
Is it possible to have my ASP Core Web API ensure the DB is migrated to the latest migration using EF Core? I know this can be done through the command line, but I want to do it programatically.
Upvotes: 98
Views: 81746
Reputation: 95
In ASP.NET Core 6 you won't have a Startup.cs
file. In previous version you had the Configure
method wich allowed you to access ServiceProvider
directly and then use GetService
method to get the Context instance and finally call the Migrate
method.
In ASP.NET Core 6 you should create a scope and resolve it out of that scope.
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
context.Database.Migrate();
}
Upvotes: 6
Reputation: 1204
In EF Core 7 you can do it in Program.cs
right after var app = builder.Build();
this way :
using (var Scope = app.Services.CreateScope())
{
var context = Scope.ServiceProvider.GetRequiredService<AppDbContext>();
context.Database.Migrate();
}
Happy coding and don't forget Up Vote ;)
Upvotes: 12
Reputation: 6755
This is a slight correction to the previous answer which created an extension method. It fixes the error that is thrown the way it was written.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace MyApp.Extensions
{
public static class IApplicationBuilderExtensions
{
public static void SyncMigrations<T>(this IApplicationBuilder app) where T : DbContext
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<T>();
context.Database.Migrate();
}
}
}
}
Upvotes: 4
Reputation: 4701
This works for me in ASP.NET Core 3.1, simply injecting the db context as a parameter to the existing Configure
method after registering it in the ConfigureServices
method.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(x => x.UseSqlite("Data Source=LocalDatabase.db"));
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
dataContext.Database.Migrate();
...
}
More details and links to full code samples available at https://jasonwatmore.com/post/2019/12/27/aspnet-core-automatic-ef-core-migrations-to-sql-database-on-startup
Upvotes: 18
Reputation: 2491
A note from documentation on the call to db.Database.EnsureCreated()
:
Note that this API does not use migrations to create the database. In addition, the database that is created cannot be later 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.
You may just want to call db.Database.Migrate()
.
Comment taken from source found above declaration here.
Upvotes: 60
Reputation: 104801
Starting .NET Core 2 using C# 7.1, you can have an asynchronous Main
method to your app, so you can call all initialization logic before you run the host, right after it has finished building:
public class Program
{
public static async Task Main(string[] args)
{
//first build
var host = CreateHostBuilder(args).Build();
//initialize
using (var serviceScope = host.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
var isDevelopment =
serviceProvider.GetRequiredService<IWebHostEnvironment>().IsDevelopment();
using var context = serviceProvider.GetRequiredService<AppDbContext>();
if (isDevelopment)
await context.Database.EnsureCreatedAsync();
else
await context.Database.MigrateAsync();
if (isDevelopment)
{
using var userManager =
serviceProvider.GetRequiredService<UserManager<AppUser>>();
await userManager
.CreateAsync(new AppUser { UserName = "dummy", Email = "[email protected]" },
password: "1234");
}
}
//now run
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Upvotes: 5
Reputation: 2525
I followed the IStartupFilter
approach to have a generic way for migrating any context.
public class DataContextAutomaticMigrationStartupFilter<T> : IStartupFilter
where T : DbContext
{
/// <inheritdoc />
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
using (var scope = app.ApplicationServices.CreateScope())
{
scope.ServiceProvider.GetRequiredService<T>().Database.SetCommandTimeout(160);
scope.ServiceProvider.GetRequiredService<T>().Database.Migrate();
}
next(app);
};
}
}
Now we're able to register the DataContexts and migration in the following way:
1st context
services.AddDbContext<ConsumerDataContext>(options => options.UseSqlServer(configuration.GetConnectionString("ConsumerConnection")), ServiceLifetime.Transient);
services.AddTransient<IStartupFilter, DataContextAutomaticMigrationStartupFilter<ConsumerDataContext>>();
2nd context
services.AddDbContext<UserDataContext>(options => options.UseSqlServer(configuration.GetConnectionString("UserConnection")), ServiceLifetime.Transient);
services.AddTransient<IStartupFilter, DataContextAutomaticMigrationStartupFilter<UserDataContext>>();
..and so on..
The culprit of IStartupFilter
is that it only allows synchronous execution of code. For database migrations this is not an issue since we have a synchronous Migrate()
method.
Upvotes: 6
Reputation: 1855
This code works in .NET core 3.0
using (var scope = app.ApplicationServices.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<T>();
dbContext.Database.Migrate();
}
Upvotes: 5
Reputation: 1488
I did this to migrate programmatically with EF Core 2.1.2 & SQL Server, based on previous answers here and bailando bailando's answer on "How and where to call Database.EnsureCreated and Database.Migrate?":
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
namespace MyApp
{
public class Startup
{
// ... (only relevant code included) ...
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyAppContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyAppContext")));
// ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
using (var serviceScope = app.ApplicationServices.CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<MyAppContext>();
context.Database.Migrate();
}
// ...
}
}
}
The project using this code is available at Github.
Upvotes: 1
Reputation: 2640
Based on chintan310's answer, here is how I migrate the database. This ensures separation of database-related tasks into Program.cs
:
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetService<AppDbContext>();
context.Database.Migrate();
var seeder = scope.ServiceProvider.GetService<AppSeeder>();
seeder.Seed().Wait();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
private static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
Upvotes: 5
Reputation: 394
Use below code to run migration at
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<YourContext`enter code here`>();
context.Database.Migrate();
}
}
Upvotes: 35
Reputation: 866
Based on the answer of @steamrolla I would propose the following improvement:
public static class EnsureMigration
{
public static void EnsureMigrationOfContext<T>(this IApplicationBuilder app) where T:DbContext
{
var context = app.ApplicationServices.GetService<T>();
context.Database.Migrate();
}
}
With this you can also ensure the migration of different contexts, e.g. if you have a Identity database.
Usage:
app.EnsureMigrationOfContext<context>();
Upvotes: 24
Reputation: 2687
You can use
db.Database.EnsureCreated();
to get your db up to date with your current model. If you want to enable migrations (If subsequent migrations are suspected), then use
db.Database.Migrate();
and put your subsequent migrations over time.
Upvotes: 54