Firebird+EF Code First:多表Generator重复问题及Code First规避方法咨询
我之前也遇到过这个问题,确实挺头疼的——Entity Framework默认情况下,会给同数据库内的自增ID共享序列(尤其是使用Oracle、PostgreSQL这类依赖序列的数据库时,或者SQL Server显式指定了共享序列),导致两张表的ID交叉跳号。不过完全可以在Code First阶段就规避这个问题,不用事后去数据库改生成器和触发器,下面给你几种可行的方案:
方案1:使用Data Annotations(EF Core 2.0+适用)
如果你的项目基于EF Core,可以直接在实体类的ID属性上通过特性指定专属序列,Code First迁移时会自动为每张表创建独立序列:
public class Master { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Sequence("Master_Id_Sequence")] // 为Master表指定专属序列 public int Id { get; set; } // 其他业务属性... } public class Details { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Sequence("Details_Id_Sequence")] // 为Details表指定另一专属序列 public int Id { get; set; } // 其他业务属性... }
这样插入数据时,两张表会各自使用自己的序列,ID就会分别从1开始连续递增,不会出现交叉跳号的情况。
方案2:使用Fluent API(EF6/EF Core通用,更灵活)
如果是EF6项目,或者你觉得Data Annotations限制太多,推荐在DbContext的OnModelCreating方法里用Fluent API配置:
针对EF Core
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Master表ID使用独立序列,起始值1,步长1 modelBuilder.Entity<Master>() .Property(m => m.Id) .UseSequence("Master_Id_Sequence") .HasSequenceOptions(startValue: 1, incrementBy: 1); // 配置Details表ID使用另一独立序列 modelBuilder.Entity<Details>() .Property(d => d.Id) .UseSequence("Details_Id_Sequence") .HasSequenceOptions(startValue: 1, incrementBy: 1); }
针对EF6(Oracle等依赖序列的数据库)
EF6本身不会自动创建序列,需要手动在迁移文件里补充创建语句,先在配置里指定序列关联:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { // 绑定Master表ID到专属序列 modelBuilder.Entity<Master>() .Property(m => m.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) .HasColumnAnnotation("SequenceName", "MASTER_ID_SEQ"); // 绑定Details表ID到另一专属序列 modelBuilder.Entity<Details>() .Property(d => d.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) .HasColumnAnnotation("SequenceName", "DETAILS_ID_SEQ"); }
然后在生成的迁移文件的Up方法里添加创建序列的SQL:
CREATE SEQUENCE MASTER_ID_SEQ START WITH 1 INCREMENT BY 1; CREATE SEQUENCE DETAILS_ID_SEQ START WITH 1 INCREMENT BY 1;
额外提醒:SQL Server的特殊情况
如果你的数据库是SQL Server,默认每张表的IDENTITY标识列是独立的,不会共享ID——除非你手动把两张表的ID绑定到了同一个序列。如果是这种情况,你可以改回默认的IDENTITY模式:
// EF Core中配置使用IDENTITY而非序列 modelBuilder.Entity<Master>() .Property(m => m.Id) .UseIdentityColumn(); modelBuilder.Entity<Details>() .Property(d => d.Id) .UseIdentityColumn();
这样两张表会各自拥有独立的自增标识,ID自然会分别从1开始连续递增。
核心思路就是让两个表的自增ID使用各自独立的序列/标识列,从Code First层面提前配置好,就能彻底避免后续数据库端的修改操作。
内容的提问来源于stack exchange,提问作者Fernando Donizeti Ito

