如何在PostgreSQL中为EF Core 5配置支持备份恢复的并发令牌/行版本
实现可备份保留的并发检查功能方案
PostgreSQL的xmin是系统级事务标识列,属于元数据,备份恢复时必然会被重写,无法保留原值,你可以改用用户自定义的并发令牌列实现相同的行版本控制能力,自定义列属于普通业务列,pg_dump/pg_restore时会完整保留原值。
下面给出两种落地实现:
方案1:EF Core应用层自动维护(无需写数据库SQL)
步骤1:修改实体类,新增自定义版本列
public class Car { public Guid ID { get; set; } public string Model { get; set; } // 新增自定义并发版本列 public int RowVersion { get; set; } }
步骤2:修改OnModelCreating配置
modelBuilder.Entity<Car>(b => { b.Property(nameof(Car.ID)).IsRequired(); // 配置自定义列为并发令牌,新增/更新时自动生成值 b.Property(nameof(Car.RowVersion)) .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate(); b.ToTable("cars").HasKey(nameof(Car.ID)); });
步骤3:重写SaveChanges/SaveChangesAsync方法,自动更新版本号
在你的DbContext子类中添加如下逻辑:
public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries<Car>()) { if (entry.State == EntityState.Added) { entry.Entity.RowVersion = 1; } else if (entry.State == EntityState.Modified) { entry.Entity.RowVersion += 1; } } return base.SaveChanges(); } // 异步方法同理重写即可
该方案所有逻辑都在应用层实现,不需要修改数据库结构额外加触发器,适合小项目快速落地。
方案2:数据库端自动维护(更可靠,避免应用层漏更)
如果存在非EF Core的程序直接操作数据库的场景,推荐用数据库触发器自动维护版本列,保证所有更新场景下版本号都会更新。
步骤1:修改实体类,新增版本列
public class Car { public Guid ID { get; set; } public string Model { get; set; } public uint Version { get; set; } }
步骤2:数据库创建版本自增函数和触发器
执行如下SQL:
-- 新建版本号自增函数 CREATE OR REPLACE FUNCTION increment_row_version() RETURNS TRIGGER AS $$ BEGIN NEW.version = OLD.version + 1; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 给cars表绑定更新触发器 CREATE TRIGGER trigger_car_increment_version BEFORE UPDATE ON cars FOR EACH ROW EXECUTE FUNCTION increment_row_version();
步骤3:EF Core配置
modelBuilder.Entity<Car>(b => { b.Property(c => c.ID).IsRequired(); // 配置为并发令牌,插入时默认值为1,更新时由数据库自动生成新值 b.Property(c => c.Version) .IsConcurrencyToken() .HasDefaultValue(1) .ValueGeneratedOnAddOrUpdate(); b.ToTable("cars").HasKey(c => c.ID); });
两种方案的并发冲突检测逻辑和原来用UseXminAsConcurrencyToken完全一致,更新时EF Core会自动在WHERE条件中带上原始版本号,匹配不到行时抛出DbUpdateConcurrencyException,原有业务代码不需要做大幅修改。
内容的提问来源于stack exchange,提问作者hunterex
相关产品推荐
相关产品推荐

