当前位置: 首页 > news >正文

02020404 EF Core基础04-自增主键、Guid主键、混合自增、Hi/Lo算法、Migration深入、数据库其它迁移命令

02020404 EF Core基础04-自增主键、Guid主键、混合自增、Hi/Lo算法、Migration深入、数据库其它迁移命令

1. 主键无小事(视频3-6)

1.1 自增主键
1、EF Core支持多种主键生成策略:自动增长;Guid;Hi/Lo算法等。
2、自动增长。优点:简单;缺点:数据库迁移以及分布式系统中比较麻烦;并发性能差。long、int等类型主键,默认是自增。因为是数据库生成的值,所以SaveChanges后会自动把主键的值更新到Id属性。试验一下。场景:插入帖子后,自动重定向帖子地址。
3、自增字段的代码中不能为Id赋值,必须保持默认值0,否则运行的时候就会报错。
1.2 重定向到主键示例
// Dog.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace EFCoreDemo
{public class Dog{public long Id { get; set; } //主键public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;namespace EFCoreDemo
{class MyDbContext : DbContext{public DbSet<Dog> Dogs { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序包管理控制台:
PM> add-migration adddog
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250921003202_adddog'.
Done.
PM> 
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// System.cs
using System;
using System.Threading.Tasks;
using System.Linq;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){using (MyDbContext ctx = new MyDbContext()){Dog dog01 = new Dog();dog01.Name = "abc";Console.WriteLine(dog01.Id); // 插入数据库之前Idctx.Dogs.Add(dog01); // Dogs表插入数据await ctx.SaveChangesAsync(); // 更新数据库Console.WriteLine(dog01.Id); // 插入数据库之后Id}Console.ReadLine();}}
}控制台输出:
0
1说明:SaveChanges之后,会自动把主键的值更新到Id属性。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序执行后Dogs表的结果:
Id	Name
1	abc

2. Guid主键

2.1 Guid算法介绍
1、Guid算法(或UUID算法)生成一个全局唯一的Id。适合于分布式系统,在进行多数据库数据合并的时候很简单。优点:简单,高并发,全局唯一;缺点:磁盘空间占用大。
2、Guid值不连续。使用Guid类型做主键的时候,不能把主键设置为聚集索引。因为聚集索引是按照顺序保存主键的,因此用Guid做主键性能差。比如MySQL的InnoDB引擎中主键是强制使用聚集索引的。有的数据库支持部分的连续Guid,比如SQLServer中的NewSequentialId(),但也不能解决问题。在SQLServer等中,不要把Guid主键设置为聚集索引;在MySQL中,插入频繁的表不要用Guid做主键。
3、演示Guid用法:既可以让EF Core给赋值,也可以手动赋值(推荐)。
2.2 C#中的Guid结构体
  • C#中的Guid是一个结构体类型。
using System;namespace EFCoreDemo
{class Program{static void Main(string[] args){Guid g = Guid.NewGuid(); // 使用NewGuid方法创建Guid对象Console.WriteLine(g); // 打印Guid对象Console.WriteLine(g.ToString()); // Guid对象转换为字符串Console.ReadLine();}}
}第一次运行,控制台输出:
7eca159e-8a9a-4bb8-81c5-86153c06ded2
7eca159e-8a9a-4bb8-81c5-86153c06ded2
第二次运行,控制台输出:
829af8ac-fc47-403c-8380-95250f980be4
829af8ac-fc47-403c-8380-95250f980be4
...
第n次运行,控制台输出:
620fe0f9-30b3-4d6d-8c08-115eea77bd52
620fe0f9-30b3-4d6d-8c08-115eea77bd52说明:
1. Guid算法在同一台电脑上任意时刻生成的值不会重复。
2. Guid算法在不同电脑上任意时刻生成的值也不会重复。
3. Guid算法综合了网卡地址,时间等数据综合给出了一个唯一的值。
4. Guid不需要考虑锁,多线程中的重复,因为在任何时候它的值都是唯一的。
5. Guid值用作比较,性能低。同时它占用的空间大。
2.3 EF Core使用Guid
// Dog.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace EFCoreDemo
{public class Bird{public Guid Id { get; set; } //主键public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;namespace EFCoreDemo
{class MyDbContext : DbContext{public DbSet<Bird> Birds { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序包管理控制台:
PM> add-migration addbird
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250921005202_adddog'.
Done.
PM> 
  • 通过SSMS查看数据库
图片链接丢失
  • Guid类型对应到SQL Server里面就是uniqueidentifier类型。
// 使用EF Core自动的给Guid赋值:Program.cs
using System;
using System.Threading.Tasks;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){using (MyDbContext ctx = new MyDbContext()){Bird b01 = new Bird();b01.Name = "yzk";Console.WriteLine(b01.Id); // 插入数据库之前Id,默认值:00000000-0000-0000-0000-000000000000ctx.Birds.Add(b01); // 给数据库插入数据Console.WriteLine(b01.Id); // @1 在Add之后,在更新到数据库之前,Id的Guid值已经有了。1e89ee64-dae4-4d33-5717-08ddf8bb7517await ctx.SaveChangesAsync(); // @2 更新数据库Console.WriteLine(b01.Id); // 插入数据库之后Id。1e89ee64-dae4-4d33-5717-08ddf8bb7517}Console.ReadLine();}}
}控制台输出:
00000000-0000-0000-0000-000000000000
1e89ee64-dae4-4d33-5717-08ddf8bb7517
1e89ee64-dae4-4d33-5717-08ddf8bb7517说明:
1. 在@1处,只要Add之后,EF Core引擎就会给Guid赋值。
2. 在@2处,将Guid值更新到数据库中。
3. 因此Guid值不是数据库赋值的,而是EF Core赋值的。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
using System;
using System.Threading.Tasks;namespace EFCoreDemo
{class Program{static async Task Main(string[] args){using (MyDbContext ctx = new MyDbContext()){Bird b01 = new Bird();b01.Id = Guid.NewGuid(); // 显式的给Guid赋值b01.Name = "abc";Console.WriteLine(b01.Id);ctx.Birds.Add(b01);Console.WriteLine(b01.Id); await ctx.SaveChangesAsync();Console.WriteLine(b01.Id);}Console.ReadLine();}}
}控制台输出:
2c6ccc8e-5760-49b5-8e4b-54ae560cd60c
2c6ccc8e-5760-49b5-8e4b-54ae560cd60c
2c6ccc8e-5760-49b5-8e4b-54ae560cd60c说明:没有显式指定Guid值,EF Core会自动指定一个默认的。如果有显式指定Guid值,那么就用显式指定的。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
总结:
1. 推荐使用显式指定Guid值,这样可以在Add方法之前提前确定好,便于使用。
2. 对于自增主键,在Add之后值还是为0。只有SaveChangesAsync之后才会增加,因为自增是在数据库里面完成自增的,不是由EF Core完成的。

3. 主键其它方案

3.1 混合自增和Guid(非复合主键)
  • 用自增列做物理的主键,而用Guid列做逻辑上的主键。把自增列设置为表的主键,而在业务上查询数据时候把Guid当主键用。在和其他表关联以及和外部系统通讯的时候(比如前端显示数据的标识的时候)都是使用Guid列。不仅保证了性能,而且利用了Guid的优点,而且减轻了主键自增性导致主键值可被预测带来的安全性问题。
3.2 Hi/Lo算法
  • EF Core支持Hi/Lo算法来优化自增列。主键值由两部分组成:高位(Hi)和低位(Lo),高位由数据库生成,两个高位之间间隔若干个值,由程序在本地生成低位,低位的值在本地自增生成。不同进程或者集群中不同服务器获取的Hi值不会重复,而本地进程计算的Lo则可以保证可以在本地高效率的生成主键值。但是HiLo算法不是EF Core的标准。

4. Migration深入(视频3-7)

1、使用迁移脚本,可以对当前连接的数据库执行编号更高的迁移,这个操作叫做“向上迁移”(Up),也可以执行把数据库回退到旧的迁移,这个操作叫“向下迁移”(Down)。
2、除非有特殊需要,否则不要删除Migrations文件夹下的代码。
3、进一步分析Migrations下的代码。分析Up、Down等方法。查看Migration编号。
4、查看数据库的__EFMigrationsHistory表:记录当前数据库曾经应用过的迁移脚本,按顺序排列。
4.1 初始的EF Core代码
  • 先将当前CoreDataDB数据库中所有数据表清空
// Person.cs
namespace EFCoreDemo
{class Person{public long Id { get; set; } //主键public string Name { get; set; }//标题}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// PersonConfig.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;namespace EFCoreDemo
{class PersonConfig : IEntityTypeConfiguration<Person>{public void Configure(EntityTypeBuilder<Person> builder){builder.ToTable("T_Persons");builder.Property(p => p.Name).IsRequired();}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// MyDbContext.cs
using Microsoft.EntityFrameworkCore;namespace EFCoreDemo
{class MyDbContext : DbContext{public DbSet<PersonConfig> Dogs { get; set; }public DbSet<Person> Birds { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){string connStr = "Server=.;Database=CoreDataDB;Trusted_Connection=True;MultipleActiveResultSets=true";optionsBuilder.UseSqlServer(connStr);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// Program.cs
using System;
using System.Threading.Tasks;namespace EFCoreDemo
{class Program{static void Main(string[] args){Console.ReadLine();}}
}
4.2 演示Migration的迁移
  • step1 → 初始的migration,并更新到数据库。
// 程序包管理器控制台
PM>  add-migration init
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> update-database
Build started...
Build succeeded.
Applying migration '20250921035714_init'.
Done.
PM> 
  • step2 → 查看解决方案资源管理器中Migration文件夹下的20250921035714_init下两个文件
// 执行step1中的代码之后,解决方案资源管理器会自动生成迁移脚本:20250921035714_init.cs
using Microsoft.EntityFrameworkCore.Migrations;namespace EFCoreDemo.Migrations
{public partial class init : Migration{protected override void Up(MigrationBuilder migrationBuilder) // @1.1 往上迁移{migrationBuilder.CreateTable( // @1.2 创建表name: "T_Persons",columns: table => new{Id = table.Column<long>(type: "bigint", nullable: false).Annotation("SqlServer:Identity", "1, 1"),Name = table.Column<string>(type: "nvarchar(max)", nullable: false)},constraints: table =>{table.PrimaryKey("PK_T_Persons", x => x.Id);});}protected override void Down(MigrationBuilder migrationBuilder) // @2.1 往下迁移{migrationBuilder.DropTable( // @2.2 删除表name: "T_Persons");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
20250921035714_init.Designer.cs
// <auto-generated />
using EFCoreDemo;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;namespace EFCoreDemo.Migrations
{[DbContext(typeof(MyDbContext))][Migration("20250921035714_init")]partial class init{protected override void BuildTargetModel(ModelBuilder modelBuilder){
#pragma warning disable 612, 618modelBuilder.HasAnnotation("Relational:MaxIdentifierLength", 128).HasAnnotation("ProductVersion", "5.0.4").HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);modelBuilder.Entity("EFCoreDemo.Person", b =>{b.Property<long>("Id").ValueGeneratedOnAdd().HasColumnType("bigint").HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);b.Property<string>("Name").IsRequired().HasColumnType("nvarchar(max)");b.HasKey("Id");b.ToTable("T_Persons");});
#pragma warning restore 612, 618}}
}说明:非必要(即使不用了)也不要将Migration中的cs文件删除,便于回退。
  • step3 → 给Person类增加一个属性
// Person.cs
namespace EFCoreDemo
{class Person{public long Id { get; set; } //主键public string Name { get; set; }//标题public double Height { get; set; } // 身高}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序包管理器控制台
PM>  add-migration addheight
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM>
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 执行step1中的代码之后,解决方案资源管理器会自动生成迁移脚本:20250921044404_addheight.cs
using Microsoft.EntityFrameworkCore.Migrations;namespace EFCoreDemo.Migrations
{public partial class addheight : Migration{protected override void Up(MigrationBuilder migrationBuilder){migrationBuilder.AddColumn<double>( // 增加1列name: "Height",table: "T_Persons",type: "float",nullable: false,defaultValue: 0.0);}protected override void Down(MigrationBuilder migrationBuilder){migrationBuilder.DropColumn( // 删除1列name: "Height",table: "T_Persons");}}
}
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 程序包管理器控制台
PM> update-database
Build started...
Build succeeded.
Applying migration '20250921044404_addheight'.
Done.
PM> 
  • step4 → 通过SSMS查看__EFMigrationsHistory表
图片链接丢失
MigrationId	             ProductVersion
20250921035714_init		     5.0.4
20250921044404_addheight	 5.0.4说明:
1. MigrationId → 与解决方案资源管理器中的迁移脚本对应
2. ProductVersion → 当前EF Core的版本
  • step5 → 此时如果通过SSMS主动删除数据表20250921044404_addheight这一项,重新再执行update-database会报错。
    • 因为数据表中已经删了20250921044404_addheigh,此时执行update-database实际执行的是20250921035714_init.cs这一版。
    • 在数据表中已经有增加的height这一列,而20250921035714_init.cs这一版没有Height属性,因此会冲突产生错误。
  • step6 → 通过SSMS将删除的数据重新恢复到数据表中。拆了 → 修好。
4.3 总结
  • 资源管理器中的Migration和__EFMigrationsHistory数据表非必要不要删除,最好保留。

5. 数据库其它迁移命令(视频3-8)

1、Update-Database XXX 
把数据库恢复到XXX的状态,迁移脚本不动。
2、Remove-migration
删除最后一次的迁移脚本
3、Script-Migration
生成迁移SQL代码。有了Update-Database 为什么还要生成SQL脚本。
可以生成版本D到版本F的SQL脚本:
Script-Migration D F
生成版本D到最新版本的SQL脚本:Script-Migration D

结尾

书籍:ASP.NET Core技术内幕与项目实战

视频:https://www.bilibili.com/video/BV1pK41137He

著:杨中科

ISBN:978-7-115-58657-5

版次:第1版

发行:人民邮电出版社

※敬请购买正版书籍,侵删请联系85863947@qq.com※

※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

http://www.hskmm.com/?act=detail&tid=12476

相关文章:

  • 02020403 EF Core基础03-Fluent API、Data Annotation、两种配置的选择
  • Java中异步任务的执行方式有几种?
  • 广二联考题解补全计划:
  • Chapter 8 Contour / Shape Detection
  • 【左程云算法笔记016】双端队列-双链表和固定数组实现 - 教程
  • java相关问题:面向对象入门2与类的识别
  • EXCEL自动调整列宽的快捷键
  • 【C++实战⑬】解锁C++文件操作:从基础到实战的进阶之路 - 实践
  • 破解塔吊顶升高危难题!让事故率降 50%、审批快 70%
  • logicFlow________文档2
  • CF2086D Even String
  • logicflow___文档3
  • 2025年运营商API安全建设最佳实践:某头部省级电信案例解析与方案推荐
  • 软件工程第二次作业-第一次个人编程作业
  • 面向对象入门2与类的识别
  • 202508_天山固网_to
  • jmeter分布式压测
  • 怎么屏蔽 ahref.com 上你不想看到的网站链接(垃圾外链)
  • 浅谈字典树
  • go-mapus为局域网地图协作而生
  • 《手搓动态顺序表:从数组到自动扩容的华丽转身》 - 详解
  • 板子大全
  • 通过人大金仓数据库的逻辑备份与还原功能实现数据迁移
  • 第十二节:订单普通下单、支付回调、退款、退款回调详解
  • 《原子习惯》-读书笔记7
  • 第3周预习作业
  • 《原子习惯》-读书笔记6
  • Java LTS版本进化秀:从8到21的欢乐升级之旅
  • 201912_EASER
  • 搜索百科(3):Elasticsearch — 搜索界的“流量明星”