EF Core 主键和数据库迁移以及注意事项

每日英语:

C#
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
How are you doing? Great! How are you doing? Not to bad,thanks! Take it easy~ what do you do for a living?//靠什么生活 I'm a teacher. Oh,really,what do you teach? I’m math teacher at Carson College.
主键

一般设计数据库主键 id 自增以及Guid两种常用方式。

1.普通自增long类型 基本上所有主流数据库都对自增列支持

优点:占用磁盘空间小,可读性强,

缺点:数据库迁移以及分布式系统(分库分表、数据库集群)使用很麻烦,同时在高并发的时候插入性能比较差。

2.Guid算法:根据网卡的Mac地址+时间戳等信息生成一个全球唯一id

优点:适合用于分布式系统,在进行多数据库的数据合并的时候很方便。

注意事项:

当把Guid列作为主键时,不能设置为聚集索引,因为聚集索引是按照顺序保存主键的,在插入Guid的时候,每一条插入的数据都需要找到合适的位置插入数据。当数据库较大的时候,性能极差,设置为非聚集索引即可

SQL server中主键可以设置为非聚集索引

MySQL中(使用InnoDB引擎)主键强制是聚集索引 如果使用这种方式,并且插入数据比较频繁,则不要把Guid列作为主键。

Guid 做主键时,数据插入频繁,不要设置为聚集索引

既可以EF Core 自动生成Guid 也可以我们自己生成,这种方式不需要把数据插入数据库就可以知道主键值(在某些场景下需要用到新增数据的id 这样可以不用再去查一次数据库)

c#
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
using TestDbContext ctx = new TestDbContext(); Console.WriteLine("****1*****"); Author a1 = new Author { Name = "杨中科" }; Console.WriteLine($"Add前,Id={a1.Id}"); ctx.Authors.Add(a1); Console.WriteLine($"Add后,保存前,Id={a1.Id}"); await ctx.SaveChangesAsync(); Console.WriteLine($"保存后,Id={a1.Id}"); Console.WriteLine("****2*****"); Author a2 = new Author { Name = "Zack Yang" }; a2.Id = Guid.NewGuid(); Console.WriteLine($"保存前,Id={a2.Id}"); ctx.Authors.Add(a2); await ctx.SaveChangesAsync(); Console.WriteLine($"保存前,Id={a2.Id}");

3.自增+Guid

表有2个主键(不是复合主键)用自增列作为物理主键,用Guid作为逻辑主键,物理主键是在进行表结构设计的时候,把自增列设置为主键,从表的结构上来看是看不出Guid是主键的,但是我们在与其他表关联的时候都用Guid列。保证了性能。然后减少了主键自增可被预测带来的安全性问题。

4.Hi/Lo算法

Hi高位 Lo 低位

高位由程序向服务器获取比如获取60

然后本地低位0-9依次使用(对应的主键就是60-69)本地低位用完后,再向服务器申请高位。

高位由服务器生成,保证了不同进程或者集群中不同服务器获取的高位值不会重复,而本地进程计算的低位则可以保证在本地高效率的生成主键值。因此,如果普通自增id的性能无法满足项目的要求,可以考虑Hi/Lo算法。

使用前看对应的数据库的EF Core ServiceProvider是否支持Hi/Lo算法

数据库迁移

命令

c#
  • 01
  • 02
  • 03
Add-Migration Update-datebase

每执行一次Add之后,Migrations文件夹下面都会生成2个文件

一个是数字+迁移名字+.cs 另外一个是数字+迁移名字+.Designer.cs(2个_会导致斜体)

执行一次Add-Migration 就是一次迁移,数字对应版本号,版本号是递增的,所以根据版本号,可以知道数据库迁移的历史。

版本号 1–>2向上迁移

版本号 2–>1向下迁移

没有特殊情况不要删除Migrations文件夹文件,保证数据库可以溯源

例如20220105064712_Init.cs

c#
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
public partial class Init : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Authors", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Name = table.Column<string>(type: "nvarchar(max)", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Authors", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Authors"); } }

up对应向上迁移的代码

Down对应向下迁移的代码

C#
  • 01
  • 02
  • 03
  • 04
  • 05
[DbContext(typeof(TestDbContext))]//表示作用那个DbContext(上下文) [Migration("20220105064712_Init")]//对应的版本号 partial class Init { }
注意事项

1.数据库会有一张_EFMigrationHistory 记录着当前数据库曾经应用过的迁移脚本,按照顺序排列的

没特殊情况,不要修改这张表数据。

2.由于执行Add-Migration的时候,会先编译项目,如果项目编译失败,会导致迁移失败。在此之前,需要保证项目能编译通过。

3.当解决方案有多个项目时,需要在程序包管理器控制台选中要迁移的项目

其他数据库迁移命令

1.Update-database xxx 把数据库回滚到对应的版本迁移脚本之后的状态。只是把当前连接的数据库进行回滚,迁移脚本还在。

2.Remove-migration 删除最后一次迁移脚本。

3.生成迁移脚本,Script-Migration 根据迁移代码生成SQL脚本

Script-Migration xx (版本号)

比如当前我的数据库是2版本号

我如果向下(回滚)我可以生成Script-Migration 1 对应的sql脚本

向上可以生成Script-Migration 3 对应的sql脚本

在EF Core 中还可以 context.Database.Migration()来对当前程序连接的数据库来进行迁移。

具体情况根据自己公司或者项目来决定。