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()来对当前程序连接的数据库来进行迁移。
具体情况根据自己公司或者项目来决定。