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

每日英语:

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 这样可以不用再去查一次数据库)

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算法

数据库迁移

命令

Add-Migration

Update-datebase

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

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

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

版本号 1–>2向上迁移

版本号 2–>1向下迁移

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

例如20220105064712_Init.cs

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对应向下迁移的代码

 	[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()来对当前程序连接的数据库来进行迁移。

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