注释
仅 EF4.3 及更高版本 - 实体框架 4.1 中介绍了本页中讨论的功能、API 等。 如果使用早期版本,某些或全部信息不适用。
本文介绍如何将 Code First 迁移与现有数据库配合使用,该数据库不是由 Entity Framework 创建的。
注释
本文假定你知道如何在基本场景中使用 Code First 迁移。 如果没有,则需要先阅读 Code First 迁移,然后再继续进行。
步骤 1:创建模型
第一步是创建面向现有数据库的 Code First 模型。 “代码优先到现有数据库”主题提供了有关如何执行此操作的详细指导。
注释
在对需要更改数据库架构的模型进行任何更改之前,请务必遵循本主题中的其余步骤。 以下步骤要求模型与数据库架构同步。
步骤 2:启用迁移
下一步是启用迁移。 为此,可以在包管理器控制台中运行 Enable-Migrations 命令。
此命令将在名为 Migrations 的解决方案中创建一个文件夹,并将单个类放入名为 Configuration 的其中。 Configuration 类是在为应用程序配置迁移的位置,可以在 Code First Migrations 主题中找到有关它的详细信息。
步骤 3:添加初始迁移
一旦创建并应用迁移到本地数据库后,你可能还需要将这些迁移应用到其他数据库。 例如,本地数据库可能是一个测试数据库,你最终可能还希望将更改应用于生产数据库和其他开发人员测试数据库。 此步骤有两个选项,应选择的选项取决于任何其他数据库的架构是空的,还是当前与本地数据库的架构匹配。
- 选项 1:使用现有架构作为起点。 如果将来将应用迁移的其他数据库与本地数据库当前具有相同的架构,则应使用此方法。 例如,如果本地测试数据库当前与生产数据库的 v1 匹配,并且稍后将应用这些迁移将生产数据库更新到 v2,则可以使用此方法。
- 选项 2:使用空数据库作为起点。 在将来将应用迁移的其他数据库为空(或尚不存在)时,应使用此方法。 例如,如果开始使用测试数据库开发应用程序,但不使用迁移,稍后将想要从头开始创建生产数据库,则可以使用此方法。
选项 1:使用现有架构作为起点
Code First 迁移使用最近迁移中存储的模型的快照来检测模型更改(可以在 团队环境中的 Code First 迁移中找到有关此操作的详细信息)。 假设数据库已经具备当前模型的架构,因此我们将生成一个没有操作的迁移,以当前模型进行快照。
- 在包管理器控制台中运行 Add-Migration InitialCreate –IgnoreChanges 命令。 这会创建一个空的迁移文件,并将当前模型作为快照。
- 在包管理器控制台中运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改,因此只会将一行添加到__MigrationsHistory表中,指示此迁移已应用。
选项 2:使用空数据库作为起点
在此方案中,我们需要迁移才能从头开始创建整个数据库,包括本地数据库中已存在的表。 我们将生成一个称为 InitialCreate 的迁移,其中包含用于创建当前架构的逻辑。 然后,我们将现有数据库调整为好像此迁移已被应用。
- 在包管理器控制台中运行 Add-Migration InitialCreate 命令。 这会创建迁移以创建现有架构。
- 注释掉新创建的迁移的 Up 方法中的所有代码。 这样,我们就可以“应用”到本地数据库的迁移,而无需尝试重新创建已经存在的所有表等。
- 在包管理器控制台中运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改(因为我们暂时注释掉了这些更改),因此只会将一行添加到__MigrationsHistory表中,指示此迁移已应用。
- 取消注释 Up 方法中的代码。 这意味着,将此迁移应用到将来的数据库时,本地数据库中已存在的架构将由迁移创建。
要注意的事项
对现有数据库使用迁移时,需要注意一些事项。
默认/计算名称可能与现有架构不匹配
迁移在为迁移搭建基架时显式指定列和表的名称。 不过,在应用迁移时,迁移还会为其他数据库对象计算一个默认名称。 这包括索引和外键约束。 在针对现有模式时,这些计算得出的名称可能与数据库中实际存在的名称不匹配。
以下是您需要注意的一些情况示例:
如果使用了“选项 1:使用现有架构作为起点”,请从步骤 3 开始:
- 如果模型中的未来更改需要更改或删除以不同方式命名的数据库对象之一,则需要修改基架迁移以指定正确的名称。 迁移 API 具有可选的 Name 参数,可用于执行此操作。 例如,现有架构可能有一个具有 BlogId 外键列的 Post 表,该列的索引名为 IndexFk_BlogId。 但是,默认情况下,迁移预期此索引命名为IX_BlogId。 如果您对模型进行更改导致删除此索引,则需要修改生成的 DropIndex 调用以指定 IndexFk_BlogId 名称。
如果使用了“选项 2:使用空数据库作为起点”,请从步骤 3 开始:
- 尝试对本地数据库运行初始迁移(即还原为空数据库)的 Down 方法可能会失败,因为迁移会尝试使用不正确的名称删除索引和外键约束。 这只会影响本地数据库,因为将使用初始迁移的 Up 方法从头开始创建其他数据库。 如果要将现有本地数据库降级为空状态,最简单的方法是删除数据库或删除所有表。 在此初始降级后,将使用默认名称重新创建所有数据库对象,因此此问题不会再次出现。
- 如果模型中的未来更改需要更改或删除以不同方式命名的数据库对象之一,则这不适用于现有的本地数据库 ,因为名称与默认值不匹配。 但是,它对从零开始创建的数据库不利,因为这些数据库将使用由迁移工具选择的默认名称。 你可以在本地现有数据库上手动进行这些更改,或者考虑从头开始使用Migrations重新创建数据库,就像在其他计算机上一样。
- 使用初始迁移的 Up 方法创建的数据库可能与本地数据库略有不同,因为将使用索引和外键约束的计算默认名称。 你最终可能还会有额外的索引,因为默认情况下,迁移会在外键列上创建索引 - 这在原始本地数据库中可能不是这种情况。
并非所有数据库对象都在模型中表示
迁移不会处理不属于模型的数据库对象。 这可以包括视图、存储过程、权限、不属于模型的表、其他索引等。
下面是需要注意以下事项的一些示例:
- 无论在“步骤 3”中选择的选项如何,如果模型中的未来更改需要更改或删除这些附加对象,迁移将不知道进行这些更改。 例如,如果删除了具有附加索引的列,则迁移功能无法检测到并删除该索引。 需要手动将此添加到基架迁移。
- 如果使用了“选项 2:将空数据库用作起点”,则初始迁移的 Up 方法不会创建这些附加对象。 如果需要,可以修改 Up 和 Down 方法以处理这些附加对象。 对于迁移 API 中本机不支持的对象(例如视图),可以使用 Sql 方法运行原始 SQL 来创建/删除它们。