【问题标题】:Recommended way to clean old Entity Framework Core migrations清理旧 Entity Framework Core 迁移的推荐方法
【发布时间】:2019-06-28 08:51:37
【问题描述】:

在开发我们的应用程序一段时间后,我们积累了相当多的 EFCore 数据库迁移。由于 EFCore 为每次迁移添加了整个 db 模型的快照,因此这段代码加起来相当多。经过分析,我们大约 80% 的编译时间都花在了迁移上(编译 + Roslyn 分析器)。

所以是时候清理一些旧的迁移了!但是最好的方法是什么?似乎没有任何官方指导...

我们不需要任何回滚(我们只前滚),所以这让事情变得更简单。我们确实需要支持从头开始创建数据库,以及从最近几次迁移中更新数据库。

我尝试过的:

  1. 核选项似乎是删除所有迁移和模型快照,并创建一个新的初始迁移。虽然这很好,但似乎有点危险。使用这种方法,我们需要非常注意数据库模式的每个部分都是代码模型的一部分。例如,我们遇到的一个极端情况是 EFCore 还不支持检查约束。所以我们在迁移中添加了一个检查约束,但在代码模型中没有。因此,在创建新的初始迁移时,检查的约束不是其中的一部分。

  2. 作为一个实验,我尝试从所有旧迁移中删除模型快照,因为快照占了导致编译时间长的代码的 90%。我发现,EFCore 仅使用快照作为比较工具来进行新的迁移。删除快照后,旧迁移在新数据库上运行时不再执行。

那么有没有更好的方法来完成我想要的?

【问题讨论】:

  • 很奇怪,根据this,解决方案 2 应该可行。
  • 刚刚测试了解决方案 2 是否适用于 EFCore 2.0。遇到了与我描述的相同的问题(没有 Designer 文件的新数据库不运行迁移)。
  • 我在 EFCore 存储库上为解决方案 2 创建了一个问题:github.com/aspnet/EntityFrameworkCore/issues/16331
  • 第一个解决方案对我有用,但是是的,你提到了一个好点,它可能很危险。

标签: entity-framework .net-core entity-framework-core


【解决方案1】:

好的,自从提出这个问题以来,我已经对此进行了很多实验。

目前看来,实现这一点的最佳方法是选项 1。选项 2 会好得多,但在实现 this EFCore feature 之前,这对于我的用例来说并不可行(支持现有的带有迁移的数据库,并支持空数据库)。

选项 1 也有一些我偶然发现的陷阱(也许更多是我没有偶然发现的)。 所以我就是这样做的:

创建一个新的初始迁移:

  1. 确保所有现有迁移都已应用到您的数据库。我们将创建一个新的初始迁移,因此尚未应用的迁移将会丢失。
  2. 删除旧的 EFCore 迁移文件和数据库快照文件。
  3. 从数据库的当前状态创建一个新的初始迁移。 (例如通过dotnet ef migrations add Initial-PostCleanup。)

此新迁移仅与新数据库兼容,因为它将创建所有表(如果任何表、约束等已存在,则会失败)。所以现在我们要让这个迁移与现有的数据库兼容:

  1. 通过dotnet ef migrations script -o script.sql为新的初始迁移创建一个 SQL 脚本。
  2. 删除第一个事务(直到第一个 GO),这将创建 __EFMigrationsHistory 表:
IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
BEGIN
    CREATE TABLE [__EFMigrationsHistory] (
        [MigrationId] nvarchar(150) NOT NULL,
        [ProductVersion] nvarchar(32) NOT NULL,
        CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
    );
END;

GO
  1. 删除在__EFMigrationsHistory 表中插入新条目的最后一个事务:
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20190704144924_Initial-PostCleanup', N'2.2.4-servicing-10062');

GO
  1. 删除 GO 命令,因为我们会将创建脚本放在 IF 语句中:
    GO\r\n\r\n 替换为空。
  2. 现在打开您的迁移文件(C# 文件,而不是 sql 文件)并将 Up 方法替换为以下内容:
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.Sql(@"
DECLARE @migrationsCount INT = (SELECT COUNT(*) FROM [dbo].[__EFMigrationsHistory])
IF @migrationsCount = 0
BEGIN
    % PASTE YOUR EDITED SQL SCRIPT HERE %
END
");
}

完成!现在一切都应该正常了!

请务必比较新数据库的数据库架构以及之前和之后的数据。如果您的 EF 代码模型不是新数据库的一部分,则所有不属于的部分。

【讨论】:

  • 完美运行,请务必从旧迁移中重新添加自定义脚本。例如,我必须添加用于创建视图的代码,否则在创建新数据库时该视图将不存在。
  • 使用此解决方案时可能会遇到一个陷阱:您将失去数据库独立性,因为复制的 SQL 现在特定于您的数据库类型。
  • 任何不只是 a.) 删除迁移 b.) 创建初始和 c.) 删除数据库表中 __migrationhistory 中除第一项外的所有项目,并更新第一条记录以具有正确的 name 和 hash ,如快照中规定的那样?
  • @Barry 你的意思是只做那三件事而不是我在答案中描述的所有事情吗?还是您的意思更具体?
  • 好吧,还没有测试,已经有一段时间了。但主要问题是确保新迁移不会尝试重新创建已经存在的表。我假设您是在数据库上手动执行步骤 c,而不是通过迁移,对吗?如果这对您来说没问题,那么您的解决方案似乎非常好。我有不能(也不想)手动修改生产数据库的限制。
【解决方案2】:

有点晚了,但我们在当前项目中遇到了同样的问题。 .Designer 中超过 400 个迁移和 600 万行代码。以下是我们设法解决此问题的方法:

MigrationProject.csproj

  <PropertyGroup>
     ...
     <DefaultItemExcludes Condition="'$(Configuration)' == 'Debug' ">$(DefaultItemExcludes);Migrations\**\*.Designer.cs</DefaultItemExcludes>
  </PropertyGroup>

这样,您无需将迁移重置为清除状态,也无需删除 .Designer 文件。您可以随时将配置更改为 Release 并通过任何必要的方式使用 .Designer 文件。

【讨论】:

  • 是的,这是给你一些时间的东西,问题的真正解决方案是“重置”,我们不能跳过它。
  • 如果只想保留最新的迁移设计器文件,请看我的回答:stackoverflow.com/a/65004969/331281
【解决方案3】:

要从头开始重置所有迁移和更新(假设磁盘上没有有用的数据),以下步骤可能有用。


(1) 确保 program.cs 文件未针对通过 Database.EnsureCreate 命令创建/更新数据库进行优化,因为此命令会阻止迁移。
(2) 删除文件夹 Migrations。
(3) dotnet 构建
(4) dotnet ef 数据库更新 0 -c yourContextFile
(5) dotnet ef 迁移添加 init -c yourContextFile
(6) dotnet ef 数据库更新 -c yourContextFile

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-27
    • 1970-01-01
    • 2017-05-16
    • 2018-11-05
    • 2020-03-09
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多