EF Core - Applying Migrations Programmatically
By FoxLearn 2/6/2025 8:24:55 AM 6
While you can apply migrations manually through the command line or tools like Package Manager Console, EF Core also provides methods to apply migrations programmatically via DbContext.Database
.
To apply any pending migrations programmatically, you can use:
await context.Database.MigrateAsync();
This method will create the database (if it doesn't exist) and apply all pending migrations.
To check for pending migrations:
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
To view applied migrations:
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync();
To apply a specific migration:
await context.GetInfrastructure().GetService<IMigrator>().MigrateAsync("Database_v4");
In this article, we’ll go over the benefits of applying migrations programmatically and show examples using EF Core’s migration methods.
Advantages of Applying Migrations Programmatically
Using the dotnet ef
command line tool requires you to install the tool and execute commands from the project folder. This can be cumbersome in production environments where developers aren’t directly involved in the deployment process.
Applying migrations programmatically is advantageous because the migration logic resides directly in the deployed code. There’s no need to deploy source code or install extra tools. Plus, if you generate SQL scripts to apply migrations, they don’t handle database creation, only table updates, which can be a limitation.
With the programmatic approach, calling MigrateAsync()
will handle both database creation and migration application, making it simpler and less error-prone.
Example of Checking for Pending Migrations and Applying Them Programmatically
Here’s an example that checks for pending migrations, applies them if any are found, and then reports the last applied migration:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; var config = new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile("appsettings.json") .AddUserSecrets<Program>() .Build(); using (var context = new LibraryContext(config.GetConnectionString("Default"))) { var pendingMigrations = await context.Database.GetPendingMigrationsAsync(); if (pendingMigrations.Any()) { Console.WriteLine($"You have {pendingMigrations.Count()} pending migrations to apply."); Console.WriteLine("Applying pending migrations now..."); await context.Database.MigrateAsync(); } var lastAppliedMigration = (await context.Database.GetAppliedMigrationsAsync()).Last(); Console.WriteLine($"You're on schema version: {lastAppliedMigration}"); }
This example uses a connection string from a configuration file and checks for pending migrations before applying them. It then reports the last migration that was applied.
How Does EF Core Know Which Migrations Have Been Applied?
EF Core tracks applied migrations using the __EFMigrationsHistory
table. You can directly query this table to see the migrations that have been applied:
SELECT [MigrationId] FROM [dbo].[__EFMigrationsHistory];
This query will return a list of migration IDs, like this:
MigrationId |
---|
20210101123000_InitialMigration |
20210214091520_AddBooksTable |
20210318073611_AddAuthorsTable |
These are the migrations that have been successfully applied to the database. The last migration in this list represents the current schema version.
Example of Applying a Specific Migration Programmatically
In some cases, you may want to apply a specific migration (for example, rolling back to an earlier schema version). EF Core makes this easy using the IMigrator
service.
Let’s assume you’re currently on Database_v6
and need to roll back to Database_v4
:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection; var config = new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile("appsettings.json") .AddUserSecrets<Program>() .Build(); using (var context = new LibraryContext(config.GetConnectionString("Default"))) { await context.GetInfrastructure().GetService<IMigrator>().MigrateAsync("Database_v4"); var lastAppliedMigration = (await context.Database.GetAppliedMigrationsAsync()).Last(); Console.WriteLine($"You're on schema version: {lastAppliedMigration}"); }
This will apply the migration Database_v4
and output the schema version as:
You're on schema version: 20210316124316_Database_v4
Notice that we only specify the name of the migration ("Database_v4"
) rather than the full timestamp.