Back to "你可能做 EF 移民错误..."

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

CI Entity Framework GitHub Migrations

你可能做 EF 移民错误...

Sunday, 23 November 2025

运行中 MigrateAsync() 启动时? 您正在给您的应用程序数据库所有者权利, 希望不会出任何差错。 有一个更好的方法 — EF 迁移捆让您将迁移作为受控的 CI 步骤, 保护您的生产应用程序的安全 。 但问题是: 有时“ 错误” 方式其实是好的。 我们来探索何时使用每种方法 。

官方文件 : 移徙概况 | 应用移民 | 套套件

"Wrong" 方式(我使用的)

此博客使用 MigrateAsync() 开始的时候我要告诉你不要使用的方法。这就是为什么我不介意, 以及为什么它可能不适合你。

在我的 Program.cs 我有以下文件:

    using (var scope = app.Services.CreateScope())
    {
        var blogContext = scope.ServiceProvider.GetRequiredService<IMostlylucidDBContext>();
        await blogContext.Database.MigrateAsync();
    }

MigrateAsync() 需要时适用待处理的迁移,并创建数据库。

  1. 启动受抚养人 移民失败了 你的应用程序启动不了
  2. 侵犯安全 - 你的应用程序需要 db_owner 右键。 您刚刚给您的运行时间应用了 键来投放表格 。

我为什么要逃避它:公共数据, 单一的多克网络, 个人项目。 你可能办不到

当运行时间移民顺利时

  • 当地dev - 快速迭代击打仪式
  • 个人项目 个人项目 - 低爆炸半径,无敏感数据
  • Dockker- compos dev 环境 - 便利赢赢
  • 原型设计 - 气候在不断变化

当他们不

  • 多个应用程序实例 - 种族条件
  • 敏感数据 - PII,财务,受管制=要求的适当离职
  • 实际用户生产 - 失败的移徙=失业

正确途径:EF捆绑

EF捆绑是一个自足的可执行文件, 包含您所编译的迁移 。 dotnet ef database update 组合成独立 .exe.

为什么捆绑赢:

  • 没有运行时间依赖性 - 目标不需要SDK或EFCLI
  • 适当离职 - 申请从不需要 db_owner;只有在部署期间才使用 CID 跑者
  • CIC可见度 - 输油管道记录显示故障,没有埋在启动应用程序中
  • 滚回安全 在错误的代码部署之前就停止部署
  • 具有同等能力者 - 跟踪应用内容,只运行需要的东西

注: 用于生产级安保,使用 管理身份 而不是连接字符串。 但捆绑仍然是从跑步移民中迈出的一大步。

GitHub 行动实例

      - name: Install EF Core tools
        run: dotnet tool install --global dotnet-ef

      - name: Add EF tools to PATH
        run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH

      - name: Generate EF migration bundle
        run: |
          dotnet ef migrations bundle \
            --project ${{ env.WEB_PROJECT }} \
            --output efbundle.exe \
            --configuration ${{ env.BUILD_CONFIGURATION }} \
            --runtime ${{ env.RUNTIME_IDENTIFIER }} \
            --context AdminDbContext \
        env:
          AdminSite__ConnectionString: ${{ secrets.PROD_SQL_CONNECTIONSTRING }}

      - name: Run EF migration bundle
        run: |
          ./efbundle.exe
        env:
          AdminSite__ConnectionString: ${{ secrets.PROD_SQL_CONNECTIONSTRING }}

捆绑从环境变量读取连接字符串, 并应用待定的迁移。 已经应用过吗 ? 它只是成功退出 。

本地套件

没有线人吗 想在推前先试一试吗?

使用案例: 在CI前测试, DBA 上交( 独立前端, 不需要 SDK ) , 中转部署, 调试 --verbose.

创建捆绑

# Install EF CLI (once)
dotnet tool install --global dotnet-ef

# Basic bundle
dotnet ef migrations bundle \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid \
    --output efbundle.exe

# Self-contained (includes runtime - portable to machines without .NET)
dotnet ef migrations bundle \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid \
    --output efbundle.exe \
    --self-contained

# Cross-platform (e.g., build on Windows, deploy to Linux)
dotnet ef migrations bundle \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid \
    --output efbundle \
    --runtime linux-x64

正在运行您的套装

# Using default connection string from appsettings.json
./efbundle.exe

# Override with a specific connection string
./efbundle.exe --connection "Host=localhost;Database=mostlylucid;Username=postgres;Password=secret"

# Using an environment variable (matches your config key)
$env:ConnectionStrings__DefaultConnection="Host=localhost;..." # PowerShell
export ConnectionStrings__DefaultConnection="Host=localhost;..." # Bash
./efbundle.exe

有用的套装选项

# See what migrations would be applied without running them
./efbundle.exe --dry-run

# Verbose output for debugging
./efbundle.exe --verbose

# Apply migrations up to a specific migration (useful for testing)
./efbundle.exe --target-migration "20231115_AddUserTable"

# Combine options
./efbundle.exe --verbose --dry-run

本地测试工作流量

# 1. Create migration
dotnet ef migrations add AddNewFeature \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid

# 2. Build bundle
dotnet ef migrations bundle \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid \
    --output efbundle.exe

# 3. Dry run first
./efbundle.exe --dry-run --verbose

# 4. Run for real
./efbundle.exe --verbose

# 5. Broken? Remove and retry
dotnet ef migrations remove \
    --project Mostlylucid.DbContext \
    --startup-project Mostlylucid

Catchs 语法错误、违反限制、FK问题 -- -- 所有 之前 CI或生产。

构建绩效

捆包的代代慢慢 - 30+秒的大型项目,不要在每一个建筑上产生。

  • 本地测试时手工生成
  • 只是在部署期间才在CI产生,而不是每个PR
  • 如果移民没有改变, 缓存捆包

如果您真的想要自动生成, 请添加一个 MSBulild 目标 :

<Target Name="BuildMigrationBundle">
  <Exec Command="dotnet ef migrations bundle --output $(OutputPath)efbundle.exe --force" />
</Target>

然后: dotnet build -t:BuildMigrationBundle

混合方法

最好的两个世界:当地方便,生产安全。

if (builder.Environment.IsDevelopment())
{
    using var scope = app.Services.CreateScope();
    var context = scope.ServiceProvider.GetRequiredService<IMostlylucidDBContext>();
    await context.Database.MigrateAsync();
}
// Production: CI pipeline runs the bundle

套装替代物

SQL 脚本

生成普通 SQL 而非可执行文件 。 对 DBA 审查和现有变革管理程序来说, 卓越 。

# All migrations
dotnet ef migrations script --output migrations.sql

# Idempotent (safe to run multiple times) - USE THIS
dotnet ef migrations script --idempotent --output migrations.sql

# Range of migrations
dotnet ef migrations script FromMigration ToMigration --output migrations.sql

专业: 完全可见度,任何SQL客户端都可以运行,版本控制方便,DBA审批工作流程。

关节 : 不自动跟踪( 使用) --idempotent),手动执行,如果修改脚本,可能会漂移。

见见 SQL 脚本上的公务 docs.

CI 中的 SQL 脚本

- name: Generate and apply migrations
  run: |
    dotnet ef migrations script --idempotent --output migrations.sql
    # SQL Server
    sqlcmd -S ${{ secrets.DB_SERVER }} -d ${{ secrets.DB_NAME }} -i migrations.sql
    # Or PostgreSQL
    PGPASSWORD=${{ secrets.DB_PASSWORD }} psql -h ${{ secrets.DB_HOST }} -f migrations.sql

DACCPAC (仅SQL服务器)

发发援援协调会基于状态的以移徙为基础的移徙人口。您根据目标数据库定义想要的图案, SqlPackage diffs 。

SqlPackage.exe /Action:Publish /SourceFile:MyDatabase.dacpac /TargetConnectionString:"..."

专业: Schema作为代码、自动生成、处理一切(表格、视图、SP、索引)、企业工具。

关节 : 仅 SQL 服务器, 两个地方的图案( EF 模型 + SQL 项目) , diff 引擎的选择有问题, 列重命名看起来像 drop+add 。

见见 Sql 软件包文档.

比较比较表

最佳办法 需要 .NET 自动追踪 应用 .DBA 友好 . . |----------|----------|---------------|---------------------|--------------|-------------------| | MigrateAsync() * 发展/小型项目 * * 是(临时) * * 是 * 否 * * 是 * * * 是 * * * 是

  • EF bundles * CI/CD 输油管 * * 无(自足) * * 是 * 有* * 有* * 有* * * 有* * * 有* * 否(自足) * * 否(自足) * * 有* * 有* * 有* * 有* * 有* * 有* * 有* * 有* * 有* * 有* QQL SQL 脚本 DBA 控制环境 --idempotent 是的,是的,是的,是的,是的。 DACPAC SQL 服务器企业

提示提示

找到设计器文件

移民在当地工作,但不在移民中心工作? 检查您所执行的两个文件 :

  • 20231115_AddUserTable.cs - 移徙法
  • 20231115_AddUserTable.Designer.cs - 模型快照

缺少设计器文件 = 沉默失败 。

多个 DbContext

dotnet ef migrations bundle --context BlogDbContext --output blog-migrations.exe
dotnet ef migrations bundle --context IdentityDbContext --output identity-migrations.exe

连接字符串优先级

  1. --connection 参数参数
  2. 环境变数
  3. appsettings.json

在 CI 中使用环境变量。

IDignTimeDbcontextFortorial 设计设计工具

EF 工具需要对 DbContext 进行即时化 DbContext 。如果您的 DbCtext 在一个单独的项目中,或者启动过程复杂,请执行 IDesignTimeDbContextFactory<T>:

public class AdminDbContextFactory : IDesignTimeDbContextFactory<AdminDbContext>
{
    public AdminDbContext CreateDbContext(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true)
            .AddEnvironmentVariables()
            .AddUserSecrets<AdminDbContextFactory>()
            .Build();

        var connectionString = config["AdminSite:ConnectionString"]
            ?? throw new InvalidOperationException("Missing connection string");

        var optionsBuilder = new DbContextOptionsBuilder<AdminDbContext>();
        optionsBuilder.UseSqlServer(connectionString, sql => sql.CommandTimeout(120));

        return new AdminDbContext(optionsBuilder.Options);
    }
}

使用时间: DbContext 在不同的项目中使用, 复杂的启动, 设计时需要用户保密 。

怎么样... ?

我收到的常见问题和退路

"为什么不只是运行 dotnet ef database update "在CI"吗?

上面写着, 但短版本是: 捆包是便携式的手工艺品。 您的部署步骤不需要 EF CLI 、 源代码或设计时间分辨率 。 同样的捆包在测试、 中转和测试中运行 - 零漂移 。

"这难道不是为了一个小应用而过度杀戮吗?"

如果你是独唱,数据是公开的 爆炸半径低 MigrateAsync() 。但当您添加第二个开发者、敏感数据或多个环境时,捆包会为自己付费。

"反弹怎么办?"

EF不自动回滚。 选项 :

  • 生成 Down() 迁移至他处,然后迁移至他所居住的地方。
  • 从备份恢复
  • 写入手动迁移以撤销更改

对于关键系统:首先对数据库克隆进行迁移测试。

"我能在库伯涅茨的集装箱里做移民吗?"

是。 套装 + Init 集装箱是一个固态模式:

initContainers:
  - name: migrate
    image: myapp:latest
    command: ["./efbundle.exe"]
    env:
      - name: ConnectionStrings__Default
        valueFrom:
          secretKeyRef:
            name: db-secrets
            key: connection-string

App 容器等待 Init 完成 。

"流利移民/DbUp/其他工具呢?"

EF捆绑是EF本地的解决方案,但是 流流者弹顶 关键区别是:这些是针对移民的工具,而EF包来自您现有的EF模式。

"我的DBA想在SQL运行前 对所有SQL进行审查"

使用使用 --idempotent 脚本 :

dotnet ef migrations script --idempotent --output migrations.sql

DBA审查和批准。

  • 手动运行脚本, 或
  • 一旦批准, 运行捆包( 做同样的事情)

"我如何处理零停机时间的迁移?"

这是一个部署战略问题,不是移民问题。

  1. 使移徙向后兼容( 添加列为无效, 不重命名)
  2. 部署新的代码,处理新旧和旧的策略
  3. 流动移徙
  4. 只使用新计划布局代码
  5. 清理( 在以后的移民中丢弃旧的列)

套装不能解决问题 只会让第三步更可预测

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.