一种更安全的 EF Core 自动迁移方案:生产环境中的可控数据库更新实践

一种更安全、可控的 EF Core 自动迁移方案:模块化架构下的工程化实践

在现代应用开发中,数据库迁移已经成为不可或缺的工程环节。
特别是在 模块化架构、分布式系统、多团队协作 的背景下,传统 EF Core 迁移虽然足够灵活,但在工程化落地方面仍存在一些现实挑战,例如:

  • 各模块拥有独立的 DbContext
  • 多人并行开发导致迁移脚本冲突
  • 生产环境通常禁止破坏性更改
  • 某些实体(例如 DTO)不应出现在数据库中
  • 不同数据库对大小写的敏感性差异会引发迁移问题

为了在保持 EF Core 使用体验的基础上增强其工程化能力,我设计并开源了一个迁移增强库 —— Daibitx.EFCore.AutoMigrate
它的目标并不是替代 EF Core,而是在其基础之上提供一种更稳健、安全且更适合模块化工程的迁移方式。

本文将从工程需求出发,系统介绍这一方案背后的设计理念、解决的核心问题,以及它在模块化项目中的实际表现。


📘 GitHub(开源地址)

https://github.com/daibitx/Daibitx.EFCore.AutoMigrate
欢迎 Star,这是对开源作者最大的支持。

📦 NuGet 包

https://www.nuget.org/packages/Daibitx.EFCore.AutoMigrate


一、为什么传统 EF Core 迁移在工程化场景不够用?

虽然 EF Core 的 Migrations 机制功能丰富,但它默认假设的场景是:

单应用、单团队、单模型、单数据库。

而当架构逐渐模块化或开始多人协作时,一些问题会不可避免地出现。

1. 多 DbContext / 多模块带来的结构管理难度

每个模块维护自己的实体、自己的上下文,一旦参与同一个数据库结构,就会变成“多方共建”模式,而 EF Core 并没有提供模块级的安全隔离。

2. 多团队并行开发容易产生迁移冲突

例如:

  • A 模块添加字段
  • B 模块删除字段

如果缺少安全边界,生产库可能遭遇不可逆的破坏。

3. 生产环境通常不允许高风险操作

包括但不限于:

  • Drop Table
  • Drop Column
  • Rename Column
  • 修改列类型

这些操作在生产环境中极其危险,哪怕在开发环境看似正常。

4. 某些类是 DTO/ReadModel,不应进入迁移系统

真实项目里有大量 DTO 类,例如:

  • UserDto
  • OrderQueryDto
  • ProductItemDTO

这些类只是:

  • 视图模型
  • 读模型
  • API 输入输出结构
  • 查询模型(CQRS)

如果误被 EF Core 管理,将导致数据库生成无意义的表。

5. 多数据库的大小写敏感差异会导致失败

PostgreSQL、MySQL 对大小写敏感,而 SQL Server 不敏感。
这会导致迁移过程出现:

  • 表名冲突
  • 索引重复
  • 列名冲突

这些通常在生产才暴露,非常危险。


二、Daibitx.EFCore.AutoMigrate 的设计目标

针对以上问题,本方案的目标十分明确:

在 EF Core 原生能力之上,构建一套更安全、更可控、更适合模块化的“增强迁移层”。

核心原则包括:

  • 默认安全,不允许破坏性操作
  • 迁移过程具备事务性
  • 支持 DTO/只读模型过滤
  • 兼容多数据库环境
  • 并发迁移安全
  • 设计时服务可模块化加载

换句话说:

它不是为了“更智能”,而是为了“更可控”。

这也是生产系统真正需要的。


三、关键能力解析

1. 安全模式(SafeMode)

SafeMode 是生产环境最核心的能力。
它会禁止:

  • Drop Table
  • Drop Column
  • Modify Column
  • Rename Table / Rename Column

只允许:

  • Add Table
  • Add Column
  • Add Index
  • Add ForeignKey

避免迁移脚本对其他团队造成破坏,是模块化架构的基础。


2. 事务式迁移执行

迁移步骤全部在数据库事务中执行:

  • 任一步骤失败则自动回滚
  • 保证迁移结果一致性
  • 避免出现“迁移中断导致数据库半结构”的问题

这在生产环境尤其关键。


3. DTO 自动过滤(工程化特性,强烈推荐)

这是基于真实工程场景加入的最实用功能之一:

任何以 Dto(大小写不敏感)结尾的实体,将自动排除在迁移之外。

源代码过滤逻辑如下:

.Where(e => !e.ClrType.Name.EndsWith("Dto", StringComparison.OrdinalIgnoreCase))

4. 并发安全

服务端集群启动时,多个实例同时执行迁移极容易导致:

  • 死锁
  • 部分实例失败

AutoMigrate 内置并发安全保护,确保迁移只执行一次。


5. 多数据库 Provider 支持

包括:

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • 以及其他支持 EF Core 的 Provider

四、使用示例

方式 1:在 ASP.NET Core 中自动迁移(推荐)

app.Services.AutoMigrate<MyDbContext>(services =>
{
    // 配置设计时服务
}, options =>
{
    options.AsSafeMode();
});

方式 2:手动迁移

context.AutoMigrate(...);

方式 3:使用 Builder 模式

var runner = new MigrateBuilder<MyDbContext>(dbContext)
    .WithOptions(new AutoMigrationOptions().AsSafeMode())
    .Build();

await runner.ExecuteAsync();

五、生产环境最佳实践

1. 强烈建议使用安全模式

options.AsSafeMode();

2. 不要直接修改列结构

请采用:

  1. 新增列
  2. 数据迁移
  3. 删除旧列(开发或测试环境执行)

3. 注意数据库大小写敏感设置

问题描述: 直接在不存在的数据库上使用 EF Core 迁移可能会生成字段不敏感的数据库表,导致后续迁移时出现字段索引名称重复等问题。

解决方案:

方案一:通过 Fluent API 配置(推荐)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // 设置所有字符串列区分大小写
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        foreach (var property in entityType.GetProperties())
        {
            if (property.ClrType == typeof(string))
            {
                property.SetCollation("SQL_Latin1_General_CP1_CS_AS"); // SQL Server
                // 或者对于 MySQL: property.SetCollation("utf8mb4_bin");
                // 或者对于 PostgreSQL: property.SetCollation("C");
            }
        }
    }
}

方案二:通过数据注解

public class MyEntity
{
    public int Id { get; set; }
    
    [Column(TypeName = "varchar(255) COLLATE SQL_Latin1_General_CP1_CS_AS")]
    public string Name { get; set; }
}

方案三:数据库层面设置

在创建数据库时指定区分大小写的排序规则:

-- SQL Server
CREATE DATABASE MyDatabase COLLATE SQL_Latin1_General_CP1_CS_AS;

-- MySQL
CREATE DATABASE MyDatabase CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

-- PostgreSQL
CREATE DATABASE MyDatabase ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C';

六、关于 DTO 过滤的设计思考

这个机制是根据大量实际项目经验总结出来的。

在模块化或 DDD 项目中,一个复杂模块通常包含:

  • 聚合根实体
  • ValueObject
  • 多个 DTO(读模型、投影模型)
  • 后台报表模型

但其中真正对应数据库表的只有一部分。

如果所有类都被 EF Core 扫描并参与迁移——

  • 迁移文件会变得巨大且无意义
  • 数据库会出现大量无用表
  • 模块之间可能因为 DTO 冲突导致迁移失败

因此,AutoMigrate 内置过滤规则,可以说是模块化数据库建模中一个非常实用、工程化的功能点。


七、总结

Daibitx.EFCore.AutoMigrate 的核心价值在于:

  • 让 EF Core 的迁移机制更适合模块化、多团队协作的体系
  • 提供清晰、可控、安全的迁移策略
  • 解决 DTO/查询模型误导致数据库结构污染的问题
  • 对生产环境更友好
  • 提供工程级的事务、安全保护与数据库兼容性支持

如果你的架构包含:

  • 多模块
  • 多 DbContext
  • 多团队协作
  • 多环境(开发、测试、生产)
  • 多数据库适配

那么 AutoMigrate 可能正是你需要的那一层“工程化补丁”。

posted @ 2025-11-16 23:16  daibitx  阅读(12)  评论(0)    收藏  举报