【EF Core】迁移与反向工程
简介
EF Core 提供两种方法来保持 EF Core 模型和数据库架构同步。至于我们应该选用哪个方法,请确定你是希望以 EF Core 模型为准还是以数据库为准。
迁移
如果希望以 EF Core 模型为准,请使用迁移。
在实际项目中,数据模型随着功能的实现而变化:添加和删除新的实体或属性,并且需要相应地更改数据库架构,使其与应用程序保持同步。 EF Core 中的迁移功能能够以递增方式更新数据库架构,使其与应用程序的数据模型保持同步,同时保留数据库中的现有数据。
当数据模型更改时,开发人员使用 EF Core 工具添加相应的迁移。EF Core 将当前模型与旧模型的快照进行比较,以确定差异,并生成迁移源文件。
生成新的迁移后,可通过多种方式将其应用于数据库。 EF Core 在一个历史记录表中记录所有应用的迁移,使其知道哪些迁移已应用,哪些迁移尚未应用。
反向工程
如果希望以数据库架构为准,请使用反向工程。
可通过将数据库架构反向工程到 EF Core 模型来生成相应的 DbContext 和实体类型。
创建和应用迁移的两种方式
dotnet cli工具
可用于windows、linux、macOS。这些命令以dotnet ef
开头
安装dotnet core cli工具:
dotnet tool install --global dotnet-ef
包管理控制台工具
只能在VisualStudio中运行,以动词开头,例如:Add-Migration
、Update-Database
需要安装Nuget包:
Install-Package Microsoft.EntityFrameworkCore.Tools
NuGet包
单个项目
如果迁移文件在启动项目中,在启动项目安装NuGet包:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
多项目
如果迁移文件在单独的项目中,迁移项目和启动项目中都要安装包Microsoft.EntityFrameworkCore.Tools
,启动项目中也可以安装包Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
包中包含Microsoft.EntityFrameworkCore.Design
Migrations目录
创建迁移后,会生成Migrations目录,里面包含以下文件:
- XXXXXXXXXXXXXX_AddCreatedTimestamp-主迁移文件。 包含应用迁移所需的操作(在 Up 中)和还原迁移所需的操作(在 Down 中)。
- MyContextModelSnapshot.cs--当前模型的快照。 用于确定添加下一迁移时的更改内容。
文件名中的时间戳有助于保证文件按时间顺序排列,以便你查看更改情况。
迁移流程
安装Nuget包
Install-Package Microsoft.EntityFrameworkCore.Tools
创建迁移文件
cli:dotnet ef migrations add InitialCreate
vs:Add-Migration InitialCreate
InitialCreate是迁移的描述名称
EF Core将在项目中创建名为“Migrations”的目录并生成一些文件
应用迁移
添加迁移后,需要对其进行部署并将其应用于数据库。 有多种策略可用于执行此操作,生产环境和开发环境使用不同的策略。
cli:dotnet ef database update
vs:Update-Database
这种应用迁移的方法非常适合本地开发,但不太适用于生产环境。生产环境生成sql脚本或自动迁移
使用代码自动迁移:
dbContext.Database.Migrate();
其他迁移操作
删除迁移
有时,你可能在添加迁移后意识到需要在应用迁移前对 EF Core 模型作出其他更改。 要删除上个迁移,删除迁移后可对模型作出其他更改,然后再次添加迁移。请使用如下命令:
cli:dotnet ef migrations remove
vs:Remove-Migration
避免删除已应用到生产数据库的任何迁移。 这样做意味着你将无法从数据库还原这些迁移,并且可能会破坏后续迁移所做的假设。
列出所有迁移
cli:dotnet ef migrations list
vs:Get-Migration
回滚
上面创建迁移后执行Update-Database
,可以将迁移应用到数据库。
Update-Database
后面加参数表示回滚到指定迁移文件,如Update-Database InitialCreate
,表示数据库回滚到InitialCreate
这个迁移,脚本不会有变化
生成迁移脚本(Script-Migration
)
上面说的应用迁移Update-Database
,只适用于开发环境,线上环境应该生成迁移脚本,并手动执行
Script-Migration
命令用来生成SQL脚本,并不执行,需要手动执行
Script-Migration
可以生成D版本到F版本之间的SQL脚本
Script-Migration D F
生成D版本到最新版本的SQL脚本:
Script-Migration D
重置所有迁移
在某些极端情况下,可能需要删除所有迁移并重新开始。 这可以通过删除 迁移 文件夹并删除数据库来轻松完成;此时,你可以创建新的初始迁移,其中将包含整个当前架构。
还可以重置所有迁移并创建单个迁移,而不会丢失数据。 这有时称为 "squashing",包括一些手动工作:
- 删除 Migrations 文件夹
- 创建新迁移并为其生成 SQL 脚本
- 在数据库中,删除迁移历史记录表中的所有行
- 将单个行插入到迁移历史记录中,记录已应用的第一个迁移,因为表已经存在。 insert SQL 是上面生成的 SQL 脚本中的最后一个操作。
自定义迁移文件
尽管 EF Core 通常会创建准确的迁移,但应始终查看代码,并确保其对应于所需的更改;
列重命名
在重命名属性时需要自定义迁移。 例如,如果将属性重命名 Name 为 FullName ,EF Core 将生成以下迁移:
migrationBuilder.DropColumn(
name: "Name",
table: "Customers");
migrationBuilder.AddColumn<string>(
name: "FullName",
table: "Customers",
nullable: true);
如果以上迁移按原样应用,则所有客户名称都将丢失。 若要重命名列,请将上面生成的迁移替换为以下内容:
migrationBuilder.RenameColumn(
name: "Name",
table: "Customers",
newName: "FullName");
当某个操作可能会导致数据丢失(例如删除某列),搭建迁移基架过程将对此发出警告。 如果看到此警告,务必检查迁移代码的准确性。
添加原始SQL
重命名列可以通过内置 API 来实现,在许多情况下,这是不可能的。 例如,我们可能希望将现有的 FirstName 和属性替换为 LastName 一个新的 FullName 属性。 EF Core 生成的迁移如下:
migrationBuilder.DropColumn(
name: "FirstName",
table: "Customer");
migrationBuilder.DropColumn(
name: "LastName",
table: "Customer");
migrationBuilder.AddColumn<string>(
name: "FullName",
table: "Customer",
nullable: true);
这会导致不需要的数据丢失。 为了传输旧列中的数据,我们会重新排列迁移并引入原始 SQL 操作,如下所示:
migrationBuilder.AddColumn<string>(
name: "FullName",
table: "Customer",
nullable: true);
migrationBuilder.Sql(
@"
UPDATE Customer
SET FullName = FirstName + ' ' + LastName;
");
migrationBuilder.DropColumn(
name: "FirstName",
table: "Customer");
migrationBuilder.DropColumn(
name: "LastName",
table: "Customer");
反向工程
根据数据库表来反向生成实体类、上下文
Scaffold-DbContext '连接字符串' 指定数据库的EF包名
sqlserver:
Scaffold-DbContext 'Data Source=.;Initial Catalog=EFCoreDB;User ID=sa;Password=123456' Microsoft.EntityFrameworkCore.SqlServer
Scaffold-DbContext命令详细
【Scaffold-DbContext "Server=localhost;Database=EFDB01;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -ContextDir DbContext -OutputDir Models -UseDatabaseNames -Context EFDB01Context】
- -ContextDir DbContext : 代表EF上下文类放在DbContex文件夹的目录下
- -OutputDir Models:代表所有的实体类放在Models文件夹的目录下
- -UseDatabaseNames:代表完全按照数据库中表名或字段来映射生成相应的实体
- -Context EFDB01Context:代表指定EF上下文的名称为“EFDB01Context”,默认的生成规则是: 数据库名+Context
- -DataAnnotations: 代表用数据注解的形式配置实体类属性,默认用的是FluentApi的形式进行配置。
- -Tables SysOperLogInfor,SysUserInfor:表示仅映射SysOperLogInfor,SysUserInfor两张表。
覆盖
如果数据变动很小,那么我们完全可以手动的去改一下代码的配置即可。
如果数据库修改内容多,手动修改代码配置会很麻烦,这个时候我们使用指令 【-Force】再次映射,即覆盖了原先的代码配置映射。
覆盖的完整版的指令:
【Scaffold-DbContext "Server=localhost;Database=MagicDB;User ID=sa;Password=123456;" Microsoft.EntityFrameworkCore.SqlServer -ContextDir DbContext -OutputDir Models -UseDatabaseNames -Context MagicDBContext -DataAnnotations -Tables SysOperLogInfor,SysUserInfor -Force】