

.NET CORE EF数据库迁移,以及迁移命令详解。code first。在不删除表的情况下保持同步

.NET CORE EF 数据库迁移需要的依赖
Install-Package Microsoft.EntityFrameworkCore.SqlServer -version 3.1.1
Install-Package Microsoft.EntityFrameworkCore.Tools -version 3.1.1
Install-Package Microsoft.EntityFrameworkCore.Design -version 3.1.1
或者使用ItemGroup的方式安装依赖
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
也不一定全部都要用到,根据实际情况来。Tools 库里边其实就包含了Design库
以前的迁移命令
Enable-Migrations –EnableAutomaticMigrations
已经过时不能使用了
新的命令
Add-Migration InitialCreate
一般第一次执行的初始化迁移,都是创建表,这些。还会生成数据库快照文件XXModelSnapshot.cs。
更新到数据库的命令
Update-Database [迁移名称]
后边的迁移名称是可选的,可以接迁移名称,例如: Update-Database AddColumn
上面都是比较简单的描述了一下,留下两个问题
1:怎么可以跳过某个迁移步骤,比如数据库已经存在的情况下,初次执行迁移命令会生成对数据库的创建,如果不跳过,会出现xx表已经存在的错误
2:Update-Database [迁移名称]接或者不接有什么不同
迁移命令详解
Add-Migration 迁移名称
执行一次迁移,创建迭代版本,相当于做一个记录,生成迁移代码。
如果该命令是首次创建,则会在执行命令的项目下生成Migrations文件夹,
创建数据库快照文件XXModelSnapshot.cs,以及本次迭代文件。如图
添加迁移时,EF会通过将数据模型与快照文件进行对比来确定已更改的内容。
使用get-help Add-Migration -detailed,可以查看到Add-Migration的使用详情。
如果是第一次执行迁移命令会生成对表的创建和删除,UP方法就是对表的创建,Down方法就是对表的删除
所以如果数据库中有表在执行Update-Databas命令的话会报错,提示表已经存在
所以如果你数据库已经在前面创建好了,可以把初次执行迁移命令生成的版本删除掉。不然就算你执行Update-Database的时候接一个迁移名称,还是会报错,因为初始化那一步你没有执行成功,就算你接迁移名称还是会去执行没有执行过成功的迁移文件,所以还是会报错,目前还没有找到一个命令可以只执行一个指定的迁移名称。估计他要这样设计也是为了让迁移记录和数据库保持一致性。
其实要解决重复创建表的问题,也不一定要删除首次创建的迁移文件,干掉迁移文件里边的Up和Down内容也是可以的,这样执行Update-Database就不会有问题了,因为不会重新在去创建一次表。(但是最好不要手动删除迁移文件,因为快照文件里边还有记录,直接手动删除可能也会造成迁移记录的错乱,所以要撤销迁移,或者删除迁移最好使用命令Remove-Migration来)
当然还有一种方法,就是向添加迁移日志表中添加想要跳过的记录,因为ef判断某一个迁移是否执行,是根据迁移表EFMigrationsHistory来的。比如你想跳过20211217011025_InitialCreate这一步,向迁移表中添加这条记录就行,
所以在执行更新数据库的时候想要跳过某步,这里说了三种方法。
1:删除对应的迁移文件
2:删除或者注释掉迁移文件里边的内容,本质和第一步其实是一样的
3:向迁移表__EFMigrationsHistory中添加需要跳过的迁移步骤
add-migration —context XXXDbContext
一个程序集下面存在多个DbContext 指定DbContext进行迁移
Update-Database [迁移名称]
更新到数据库。该命令更新所有未执行的迭代版本。调用的是迭代文件中的Up方法
Update-Database [迁移名称],后边也可以接迭代名称,就针对性执行某个迁移了
接名称完整的写法:Update-Database -Migration 迁移名称
执行成功可以在数据库看到执行的日志
输入get-help Update-Database -detailed,可以查看到Update-Database的使用详情。
上面说过,就算你接一个迁移名称,并不是只执行那一个迁移版本,前面如果有执行失败的,就算你接了一个迁移名字,还是会执行那个失败的版本。
那么问题就来了,既然接不接迁移名称都会执行前面没有执行过的或者执行失败的,那么接名称的意义在哪里呢?都是根据迁移日志来决定执不执行。
其实是这样的在你执行迁移命令接名称的时候,在这个迁移文件,以及迁移文件之前的都要成功执行,相当于这个名称是指定一个目标点,到这个点截至,如果不接迁移名称就是所有的迁移文件执不执行都是根据日志来的,而迁移日志又是执行成功才会添加的,所以就是说必须要保证到这个迁移文件为止的全部要成功才行。
所以如果你指定的迁移名称是最后一个没有执行的版本,那么接不接都一样,因为执行的截至点都是那一个,只有当有多个迁移文件没有执行的时候接不接迁移名称才有区别。
他并不会跳过中间没有执行的,或者执行失败的,而是到这个名称为止的所有迁移文件都要保证执行成功。
比如我们执行Update-Database -Migration AddColumn_LL,它会执行到这个文件为止,也就是检查前三个文件有没有被成功执行,没有成功执行或者没有执行过的都会执行。但是他并不是执行AddColumn_PP这个文件,也就是说它后面的不会管。
如果执行的是Update-Database -Migration AddColumn_PP,就会检查这里的所有迁移文件了,由于这里是时间点最后的一个迁移文件,所以接不接这个名称都一样。
接名称还有一个意义就是回归到某个版本,当然要回归到某个版本,前提是这个版本成功应 用过。比如上图的例子,假如所有版本都成功执行了,我想要回归到AddColumn这个版本就可以执行,Update-Database -Migration AddColumn,想要回归到AddColumn_LL版本,就执行Update-Database -Migration AddColumn_LL即可。这个回归版本的意思就是让数据库保持到这一步执行后的效果。所以你执行的是最后一步的话Update-Database -Migration AddColumn_PP,相当于就是最新的迁移版本了,并不是说回退到AddColumn_PP版本没有执行之前的版本,而是回到这个版本执行成功的版本。
Remove-Migration 回滚命令,回滚最后一次的操作,撤销本次更新的内容
撤销本次更新的内容,调用的是Down方法中的内容
回滚最后一次的操作,也就是当前的操作。官方解释:Removes the last migration.
查看命令详情
get-help Remove-Migration -detailed
执行的效果:
它会把这次迁移的迁移文件删除掉,而且会把快照的修改也还原。所以如果执行了迁移又想还原这次迁移就可以使用这个撤销命令。这次测试的是迁移还没有更新到数据库的情况。
如果执行Remove-Migration 报错:has already been applied to the database
他说已经成功更新到了数据库,给你做一个提示。如果你要确定更新,可以使用如下命令即可。
Remove-Migration -Force
Remove-Migration -Force:回滚最近的一次迁移记录
会把本次生成的迁移文件删除掉,还有后缀为ModelSnapshot的快照文件也撤销,因为这里边也会记录操作内容。所以如果这次迁移执行部分成功,部分失败的话,执行这个命令不会让更新数据库的还原,只会删除迁移文件,所以数据库的操作你可能需要手动去数据库处理一下(比如附表存在的情况下添加外键,因为数据问题造成了表添加成功了,外键没有添加成功的情况,自己在数据库处理一下就行也很加拿大)。
当然你可以先执行Update-Database -Migration:0 ,删除表结构
然后再执行:Remove-Migration (删除迁移文件)。当然如果迁移已经应用到了数据库这个命令要特别慎用,他好像某些情况下会把你前面执行的表都删除掉。所以完全不建议这样使用。
迁移记录,迁移文件和数据库不一致的情况下。如果在不删除表的情况下保持同步。(可以参考下面的快照文件同步法,可能还要简单一点)
正常情况下code first都是通过代码生成数据库,迁移文件,迁移记录,快照文件都应该和数据库一致,但是有些情况下,是先写了数据库,然后在写的代码,这样就会造成,迁移记录和数据库不一致了。这种情况下,如果后面又增加了表,或者更改了字段等等,在去执行数据库迁移的时候就会出现问题。(比如团队中有些人不会使用迁移命令乱整-.-)
比如数据库收手动加了一个role表,不是通过迁移文件去创建的,这个时候执行数据库更新就会提示,role表已经存在了,因为他没有在迁移文件中记录到。
解决方法:
先让role表的创建保持迁移文件和数据库一致后,在去做其他修改。但是如何保持一致呢,role表明明就不是迁移文件生成的数据库,而是手动在数据库中创建的表。其实很简单,我们上面知道原理后就可以解决这种问题了,不用在去把数据库干掉在重新执行迁移命令了。
先把后面相关修改的注释掉,比如在role表同步只会想要的操作,都注释掉,就是要让当然实体的内容和数据库的表保持一致。然后把快照文件删除掉,执行一次迁移Add-Migration -fb(名字随便取,这里我写的tb就是同步的简拼),然后这个时候我们就不去执行Update-Database更新数据库了,如果执行还是会报错的,因为数据库表已经存在了,我们的目的就是让迁移文件和数据库保持一致,所以我们要让ef认为我们这个命令是成功执行了的,我们只需要在数据库迁移记录表中手动添加一条记录即可。
然后就ok了,后面正常使用即可。
为什么要删除快照呢?如果不删除快照的话,执行后面的迁移估计没有任何内容的,因为创表这些操作已经记录在快照文件里边的。这就是为什么要做撤销操作,不能手动删除迁移文件,如果快照里边还有操作记录哇,你手动删除迁移文件,如果在执行一次Add-Migration它会什么都没有改变的。所以要像撤销操作要使用Remove-Migration命令或者Remove-Migration -Force。
-。-录了一个讲解的视频,后面才发现没有声音
数据库迁移解决方法之-快照文件同步法
比如添加了迁移记录然后更新到数据库的时候报错:重复创建字段,PreviewUrl。
问题分析。
不知道什么原因造成快照和数据库不一致,数据库里边文件管理表有PreviewUrl字段,但是快照里边没有PreviewUrl字段,所以执行迁移的时候就会这个字段已经有了导致迁移失败,所以在快照里边增加去添加一下这个字段,就好了
数据库和代码就同步了。在执行后面的操作就不会有问题了。
知道原理了,啷个方便啷个操作就行了,一句话。在进行迁移操作数据库的时候,要保证快照文件和数据库对应。
其实正常操作的话他肯定都是对应的哇,各种骚操作导致没有对应上也没有关系,[狗头]手动改一下就完事,怕的是不懂这个原理,改不了就麻烦了。知道原理了随便操作,不慌。
真正生成迁移记录里边的操作是根据快照和数据比较的,有什么改变就生成对应的操作。所以如果快照文件内容和数据库不一致的时候,在去生成一个数据库库迁移操作就会问题,比如上面提到的重复创建字段PreviewUrl的问题。
简单的总结,快照文件介绍
ef的数据库迁移,主要就是几个命令的使用,但是想要用好,需要对里边的原理比较了解。其实原理核心点就是迁移记录,迁移快照,迁移表,把这些搞清楚了,就可以很灵活的使用ef的数据库迁移了。
上面对迁移,和迁移记录表介绍得比较多。数据库快照得话,其实就是当前的一个数据的创建记录,要和数据库保持同步,才能更好的去修改数据库。
比如像快照的这样一段
记录了表的导航属性,外键等信息,当然这个文件其他地方也有这个表的字段这些信息记录。
执行迁移文件的时候就是根据这个快照文件和你的代码进行比较,才知道你进行了哪些修改,然后在生成对应的操作数据库的语句的迁移记录文件,执行update-databases的时候在根据迁移文件和数据库的迁移记录比较,在去操作数据库,整体的一个原理和执行流程大概就是这样。想要更好的理解这些原理,可以自己创建一个简单的项目,然后去修改实体,看看迁移文件,迁移记录表,快照的变化即可。
执行迁移命令的时候,除了快照和实体类对比,还会受到数据库版本,创建数据库的设置影响,比如开始连接点win下面的mysql数据库换到linux下的docker的MySQL,有些类型会不同,ef也会检查到,比如以前连接的是在win下面的mysql时间类型是datetime类型,换到了linux下的docker的MySQL时间类型执行迁移的时候就会全部变成了datetime(6)了
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)