EF Core中使用UseTpcMappingStrategy()实现各表独立自增ID
问题:使用TPC映射策略时为每个子类表配置独立ID生成机制
我正在编写Code-First数据库项目,使用UseTpcMappingStrategy()从继承类生成数据库表,希望每个子表拥有独立的ID生成机制,而非共享基类的序列。
当前配置代码如下:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("GAME"); var armorPrototype = modelBuilder.Entity<ArmorPrototype>(); armorPrototype.HasBaseType<ItemPrototype>(); armorPrototype.ToTable("ArmorPrototypes"); var itemPrototype = modelBuilder.Entity<ItemPrototype>() .UseTpcMappingStrategy(); var weaponPrototype = modelBuilder.Entity<WeaponPrototype>(); weaponPrototype.HasBaseType<ItemPrototype>(); weaponPrototype.ToTable("WeaponPrototypes"); base.OnModelCreating(modelBuilder); }
生成的迁移代码会创建共享序列:
migrationBuilder.CreateSequence( name: "ItemPrototypeSequence", schema: "GAME"); migrationBuilder.CreateTable( name: "ArmorPrototypes", schema: "GAME", columns: table => new { Id = table.Column<decimal>(type: "decimal(20,0)", nullable: false, defaultValueSql: "NEXT VALUE FOR [GAME].[ItemPrototypeSequence]"), Name = table.Column<string>(type: "nvarchar(max)", nullable: false), Value = table.Column<decimal>(type: "decimal(20,0)", nullable: false), IsUsable = table.Column<bool>(type: "bit", nullable: false), IsEquippable = table.Column<bool>(type: "bit", nullable: false), IsTradable = table.Column<bool>(type: "bit", nullable: false), IsUpgradable = table.Column<bool>(type: "bit", nullable: false), UpgradeLevel = table.Column<byte>(type: "tinyint", nullable: false), RequiredLevel = table.Column<long>(type: "bigint", nullable: false), Rarity = table.Column<int>(type: "int", nullable: false), DefenseValue = table.Column<long>(type: "bigint", nullable: false), EquippableBy = table.Column<string>(type: "nvarchar(max)", nullable: false) }, constraints: table => { table.PrimaryKey("PK_ArmorPrototypes", x => x.Id); });
请问如何跳过数据库序列,让每个表拥有独立的ID生成机制?同时疑惑使用双列键是否能解决此问题。
解决方案
1. 为每个子类配置独立的ID生成策略
EF Core在TPC模式下默认会给基类创建共享序列,要实现子类独立ID,需要手动覆盖每个子类的ID生成配置,关闭基类的序列绑定,为每个子类指定自己的ID生成方式(比如Identity自增、独立序列等)。
修改你的OnModelCreating方法,按以下方式配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("GAME"); // 配置基类使用TPC映射,清除默认的序列生成配置 var itemPrototype = modelBuilder.Entity<ItemPrototype>() .UseTpcMappingStrategy(); itemPrototype.Property(x => x.Id) .ValueGeneratedNever(); // 配置ArmorPrototype的独立ID(使用数据库自增列,每个表独立递增) var armorPrototype = modelBuilder.Entity<ArmorPrototype>(); armorPrototype.HasBaseType<ItemPrototype>(); armorPrototype.ToTable("ArmorPrototypes"); armorPrototype.Property(x => x.Id) .UseIdentityColumn(); // 配置WeaponPrototype的独立ID var weaponPrototype = modelBuilder.Entity<WeaponPrototype>(); weaponPrototype.HasBaseType<ItemPrototype>(); weaponPrototype.ToTable("WeaponPrototypes"); weaponPrototype.Property(x => x.Id) .UseIdentityColumn(); base.OnModelCreating(modelBuilder); }
如果不想用Identity自增,而是想用独立序列,也可以为每个子类创建专属序列:
// 为ArmorPrototypes创建独立序列 modelBuilder.HasSequence<decimal>("ArmorPrototypeSequence", "GAME"); armorPrototype.Property(x => x.Id) .HasDefaultValueSql("NEXT VALUE FOR [GAME].[ArmorPrototypeSequence]"); // 为WeaponPrototypes创建独立序列 modelBuilder.HasSequence<decimal>("WeaponPrototypeSequence", "GAME"); weaponPrototype.Property(x => x.Id) .HasDefaultValueSql("NEXT VALUE FOR [GAME].[WeaponPrototypeSequence]");
2. 关于双列键的疑惑
双列键(复合主键)并不是解决这个问题的正确方案。TPC映射的核心就是每个子类表拥有独立的主键,不需要额外的列来区分。使用双列键反而会增加数据冗余和查询复杂度,完全没必要。你只需要确保每个子类的ID生成逻辑独立即可,不需要修改主键结构。
内容的提问来源于stack exchange,提问作者Sarcovitz
相关产品推荐
相关产品推荐

