asp.net core系列 30 EF管理数据库架构--必备知识 迁移
一.管理数据库架构概述
ef core 提供两种主要方法来保持 ef core 模型和数据库架构同步。一是以 ef core 模型为基准,二是以数据库为基准。
(1)如果希望以 ef core 模型为准,请使用迁移。 对 ef core 模型进行更改时,此方法会以增量方式将相应架构更改应用到数据库,以使数据库保持与 ef core 模型兼容。
(2)如果希望以数据库架构为准,请使用反向工程。 使用此方法,可通过将数据库架构反向工程到 ef core 模型来生成相应的 dbcontext 和实体类型。
1.1. 迁移概述
在开发期间,数据模型发生更改后,会与数据库不同步。 虽然可以删除该数据库,让 ef 创建一个新的数据库来匹配该模型,但此过程会导致数据丢失。迁移作用是指:在 ef core 中使用迁移功能,能够以递增方式更新数据库架构,使其与应用程序的数据模型保持同步,同时保留数据库中的现有数据。
迁移包括 命令行工具和api,可协助执行以下任务:
(1) 创建迁移。 生成数据库更新的代码脚本,用来准备将应用模型同步到数据库。
(2) 更新数据库。通过“创建迁移”的代码脚本,同步数据库。
(3) 自定义迁移代码。有时需要修改或补充应用模型,并同步数据库。
(4) 删除迁移。 删除生成的迁移版本(该版本没有更新到数据库)。
(5) 还原迁移。 撤消回滚数据库更改(该版本已更新到数据库)。
(6) 生成 sql脚本。 可能需要一个脚本来更新生产数据库,或者对迁移代码进行故障排除。
(7) 在运行时应用迁移。 如果在设计期间更新和运行脚本不是最佳选项时,可在运行时调用 migrate() 方法。
2.1 安装命令工具
(1)对于visual studio开发,建议使用package manager console(程序包管理器控制台)工具,使用windows上的powershell脚本。
(2)对于其他开发环境,请选择.net core cli工具,使用dotnet命令是跨平台的。
本篇使用visual studio开发,使用package manager console工具来进行迁移管理,用powershell脚本,并附带上跨平台管理 的dotnet命令。还是使用blog和post应用模型来演示。
二. 命令演示
2.1 创建迁移
在定义初始化模型后,即应创建数据库。 若要添加初始迁移,请运行以下命令,其中initialcreate属于自定义迁移类名,它继承了dbcontext。
powershell | dotnet |
add-migration initialcreate | dotnet ef migrations add initialcreate |
关于准备工作和创建迁移注意事项这里不在说明,请参考“asp.net core 系列 20 ef基于数据模型创建数据库”。在开发中,一般都是使用多层架构,这里新建了一个实体类库efgetstarted.aspnetcore.model,在web项目的"依赖项"上右击添加引用,选择实体类库。
在实体类库上安装数据库提供程序。创建三个类bloggingcontext类(继承dbcontext)、blog实体类、post实体类。接着使用package manager console工具运行add-migration initialcreate命令。如下所图所示:
当运行了add-migration initialcreate后,将在项目中生成一个migrations 文件夹,并在其中生成三个文件,文件名中的时间戳有助于保证文件按时间顺序排列:
(1) 00000000000000_initialcreate.cs--主迁移文件。 包含应用迁移所需的操作 up() 和还原迁移所需的操作down() 。
(2) 00000000000000_initialcreate.designer.cs--迁移元数据文件。 包含 ef 所用的信息,如给实体类型构建属性、主键、外键、索引、映射到数据表,主体和依赖关系等等,如下所示:
(3) bloggingcontextmodelsnapshot.cs - 当前模型的快照。用于确定添加下一次迁移时更改的内容(用于递增更新)。
2.2 更新数据库
接下来,将迁移应用于数据库以创建架构。命令如下:
powershell | dotnet |
update-database initialcreate | dotnet ef database update initialcreate |
注意:更新到数据库,需要创建数据库的连接。项目中配置数据库连接,一般是存放在web项目的appsettings.json文件中,并在startup类中调用。这里演示是直接写死在startup类代码中,如下所示:
var connection = "data source = {ip}; initial catalog = efgetstarted.aspnetcore.newdb; user id = hsr;password =js*2015;"; services.adddbcontext<bloggingcontext> (options => options.usesqlserver(connection));
在package manager console工具中,运行update-database initialcreate命令,同步到数据库,如下所示:
2.3 自定义迁移代码
更改 ef core 模型后,数据库架构可能不同步。为使其保持最新,请再添加一个新的迁移。例如我在blog实体类,新添加了一个属性,和修改了一个属性的clr类型。
public class blog { public int blogid { get; set; } //将string 改为char public char name { get; set; } //新增一个属性 public string title { get; set; } public string url { get; set; } public icollection<post> posts { get; set; } }
运行add-migration blog_modifier,添加一个新的迁移
再运行update-database blog_modifier,同步到数据库中。char类型的name生成后默认是nvarchar(1),对于指定属性的长度,参考上篇关系数据库建模。
查看数据库的迁移历史,可以查看到迁移id migrationid,其中20190222031152_blog_modifier 是最新的迁移同步到数据库的版本。
2.3.1 空迁移
有时模型未变更,直接添加迁移也很有用处。 在这种情况下,添加新迁移会创建一个带空类的代码文件。可以自定义此迁移,执行与 ef core 模型不直接相关的操作。 可能需要通过此方式管理的一些事项包括:
(1)全文搜索
(2)函数
(3)存储过程
(4)触发器
(5)视图
通过上面操作,以code first模式下,不需要对数据库进行直接操作,在数据库建模上,dba以检查数据库架构为主。
2.4 删除迁移
有时修,项目在添加迁移后,意识到需要在应用迁移前对 ef core 模型作出其他更改。 要删除上个迁移。 删除迁移是使用了add-migration生成迁移后,还没有应用update-database同步到数据库架构。请使用如下命令。
powershell | dotnet |
remove-migration name | dotnet ef migrations remove name |
2.5 还原迁移
如果已经对数据库应用一个迁移(或多个迁移),但需将其复原(回滚),需要使用更新数据库命令,并指定回滚时的目标迁移名称。下面还原到迁移名称initialcreate状态时。运行pm> update-database initialcreate后,数据库同步,回到了最初initialcreate迁移状态,数据库blogs表还原到了当初。
2.6 生成 sql 脚本
调试迁移或将其部署到生产数据库时,生成一个 sql 脚本很有帮助。 之后可进一步检查该脚本的准确性,并对其作出调整以满足生产数据库的需求。 该脚本还可与部署技术结合使用。 基本命令如下。
powershell | dotnet |
script-migration | dotnet ef migrations script |
script-migration参数介绍:
-from<string> | 开始迁移。迁移可以通过名称或id来标识。数字0是一种特殊情况,这意味着在第一次迁移之前。默认值为0 |
-to<string> | 结束迁移。默认值为最后一次迁移。 |
-idempotent | 生成可在任何迁移时在数据库上使用的脚本。 |
-output <string> | 要将结果写入的文件。如果省略该参数,则在创建应用程序运行时文件的文件夹中使用生成的名称创建文件,例如:/obj/debug/netcoreapp2.1/ghbkztfz.sql/ |
下面的示例使用initialcreate迁移版本,创建一个sql脚本文件,输出到d盘。
pm> script-migration -from 0 -to initialcreate -output d:\initialcreate.sql
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 create table [blogs] ( [blogid] int not null identity, [name] nvarchar(max) null, [url] nvarchar(max) null, constraint [pk_blogs] primary key ([blogid]) ); go create table [posts] ( [postid] int not null identity, [title] nvarchar(max) null, [content] nvarchar(max) null, [blogid] int not null, constraint [pk_posts] primary key ([postid]), constraint [fk_posts_blogs_blogid] foreign key ([blogid]) references [blogs] ([blogid]) on delete cascade ); go create index [ix_posts_blogid] on [posts] ([blogid]); go insert into [__efmigrationshistory] ([migrationid], [productversion]) values (n'20190222024519_initialcreate', n'2.2.1-servicing-10028'); go
2.7 在运行时应用迁移
某些应用程序可能希望在启动或首次运行期间在运行时应用迁移。使用该migrate()
方法执行此操作。
mydbcontext.database.migrate();
注意:此方法并不适合所有场景。 尽管此方法非常适合具有本地数据库的应用,但是大多数应用程序需要更可靠的部署策略,例如生成 sql 脚本。
请勿在 migrate() 前调用 ensurecreated()。 ensurecreated() 会绕过迁移创建架构,这会导致 migrate() 失败。
参考文献: