简述
- 文章内容基于:
.NET6
Entity Framewor kCore 7.0.*
使用 EF Core 进行 Code First 开发的时候,肯定会遇到将迁移更新到生产数据库这个问题,大多数都是使用命令生成迁移 SQL,然后使用 SQL 脚本将更新迁移到生产数据库的方式,这也是官方推荐做法,毕竟专人专事嘛。
当时凡是都有例外,EF Core 自身提供了迁移API。
API Summary
API 均
DatabaseFacade
扩展方法,基类对象DbContext.Database
就是DatabaseFacade
类型。这里只简述同步方法,异步方法同理。
GetMigrations
获取所有迁移
/// <summary>
/// Gets all the migrations that are defined in the configured migrations assembly.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-migrations">Database migrations</see> for more information and examples.
/// </remarks>
/// <param name="databaseFacade">The <see cref="DatabaseFacade" /> for the context.</param>
/// <returns>The list of migrations.</returns>
public static IEnumerable<string> GetMigrations(this DatabaseFacade databaseFacade)
=> databaseFacade.GetRelationalService<IMigrationsAssembly>().Migrations.Keys;
GetPendingMigrations
获取待迁移列表
/// <summary>
/// Gets all migrations that are defined in the assembly but haven't been applied to the target database.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-migrations">Database migrations</see> for more information and examples.
/// </remarks>
/// <param name="databaseFacade">The <see cref="DatabaseFacade" /> for the context.</param>
/// <returns>The list of migrations.</returns>
public static IEnumerable<string> GetPendingMigrations(this DatabaseFacade databaseFacade)
=> GetMigrations(databaseFacade).Except(GetAppliedMigrations(databaseFacade));
GetAppliedMigrations
获取执行了迁移的列表
/// <summary>
/// Gets all migrations that have been applied to the target database.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-migrations">Database migrations</see> for more information and examples.
/// </remarks>
/// <param name="databaseFacade">The <see cref="DatabaseFacade" /> for the context.</param>
/// <returns>The list of migrations.</returns>
public static IEnumerable<string> GetAppliedMigrations(this DatabaseFacade databaseFacade)
=> databaseFacade.GetRelationalService<IHistoryRepository>()
.GetAppliedMigrations().Select(hr => hr.MigrationId);
Migrate
执行迁移
/// <summary>
/// Applies any pending migrations for the context to the database. Will create the database
/// if it does not already exist.
/// </summary>
/// <remarks>
/// <para>
/// Note that this API is mutually exclusive with <see cref="DatabaseFacade.EnsureCreated" />. EnsureCreated does not use
migrations
/// to create the database and therefore the database that is created cannot be later updated using migrations.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-migrations">Database migrations</see> for more information and examples.
/// </para>
/// </remarks>
/// <param name="databaseFacade">The <see cref="DatabaseFacade" /> for the context.</param>
public static void Migrate(this DatabaseFacade databaseFacade)
=> databaseFacade.GetRelationalService<IMigrator>().Migrate();
迁移思路
- 使用 cli 命令 Add-Migration migrationName 生成迁移文件;
- 迁移查询API查询待迁移文件;
- 调用迁移API执行迁移;
为什么要执行 cli 来生成迁移文件而不是连同迁移文件的生成也做成自动的呢?
主要原因是,一般数据库结果发生变动都是在开发阶段,此时使用 cli 更加的方便也更不容易出现因为代码逻辑而造成的 bug,从而降低开发成本。并且生成的迁移文件在生产环境下执行也是可以保证数据库更新可以依赖版本内容变动而变动的。
using Microsoft.EntityFrameworkCore;
using YJCA.Blog.EntityFrameWorkCore.EntityFrameworkCore;
Console.WriteLine("Entity Framework Core Migrate Start !\nGet Pending Migrations...");
using (BlogDbContext dbContextFactory = new BlogDbContext())
{
var dbContext = dbContextFactory.Database;
// 是否存在待迁移
if (!dbContext.GetPendingMigrations().Any())
Console.WriteLine("No to be migrated");
else
{
Console.WriteLine($"Pending Migrations:\n{string.Join("\n", await dbContext.GetPendingMigrationsAsync())}");
Console.WriteLine("Do you want to continue?(Y/N)");
if (Console.ReadLine().Trim().Equals("y", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Migrating...");
try
{
await dbContext.MigrateAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
}
Console.WriteLine("Entity Framework Core Migrate Complete !\nPress any key to exit !");
Console.ReadKey();
运行: