Code First模式下实现SQL系统版本属性与表历史及并发冲突控制
Code First实现SQL系统版本与并发冲突检测
一、修正实体类定义(解决语法与命名错误)
原实体类存在属性名空格、语法问题,先修正为符合C#规范的代码:
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; public class Employee { [Key] public int PkEmployee { get; set; } public string UserName { get; set; } public string Mobile { get; set; } public bool MobileIsActive { get; set; } public string Email { get; set; } public bool EmailIsActive { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public string Password { get; set; } // 并发冲突检测令牌:SQL Server自动维护的行版本 [Timestamp] [ConcurrencyCheck] public byte[] RowVersion { get; set; } } public class EmployeeHistory { [Key] public int PkEmployee { get; set; } public string UserName { get; set; } public string Mobile { get; set; } public bool MobileIsActive { get; set; } public string Email { get; set; } public bool EmailIsActive { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public string Password { get; set; } }
二、并发冲突检测实现
通过[Timestamp]标记的RowVersion字段,SQL Server会自动在记录变更时更新其值。当查询后记录被其他进程修改,保存操作会触发并发异常,以此阻止脏写。
1. 配置DbContext
注册实体并配置数据库连接:
public class AppDbContext : DbContext { public DbSet<Employee> Employees { get; set; } public DbSet<EmployeeHistory> EmployeeHistories { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("你的数据库连接字符串"); } }
2. 捕获并发异常
在更新操作中捕获DbUpdateConcurrencyException,抛出错误并终止保存:
public void UpdateEmployee(Employee updatedEmployee) { using var context = new AppDbContext(); try { context.Employees.Update(updatedEmployee); context.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { throw new InvalidOperationException("该员工记录在编辑期间已被他人修改,请重新查询后再操作。", ex); } }
三、系统版本历史表实现
利用SQL Server的系统版本表功能,EF Core 5+支持通过Fluent API配置,自动在主表记录变更时生成历史条目,通过StartDate和EndDate追踪数据存续周期。
1. 在DbContext中配置系统版本
重写OnModelCreating方法,绑定主表与历史表的系统版本关系:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Employee为系统版本表,指定起止时间字段 modelBuilder.Entity<Employee>() .ToTable("Employees", b => b.IsTemporal()) .Property(e => e.StartDate) .IsTemporalStartTime() .ValueGeneratedOnAddOrUpdate(); modelBuilder.Entity<Employee>() .Property(e => e.EndDate) .IsTemporalEndTime() .ValueGeneratedOnAddOrUpdate(); // 关联历史表 modelBuilder.Entity<Employee>() .ToTable("Employees", b => b.IsTemporal(t => t.HasHistoryTable("EmployeeHistories"))); // 配置历史表复合主键(同一主键可对应多条历史记录) modelBuilder.Entity<EmployeeHistory>() .HasKey(e => new { e.PkEmployee, e.EndDate }); }
2. 生成数据库结构
执行EF Core迁移命令,创建包含系统版本表的数据库:
Add-Migration AddTemporalTableForEmployee Update-Database
3. 查询历史记录
当主表记录变更时,旧版本数据会自动写入历史表,可通过以下方式查询:
public List<EmployeeHistory> GetEmployeeHistory(int employeeId) { using var context = new AppDbContext(); return context.EmployeeHistories .Where(h => h.PkEmployee == employeeId) .OrderByDescending(h => h.EndDate) .ToList(); }
注意事项
- 系统版本表仅支持SQL Server 2016及以上版本;
StartDate和EndDate由SQL Server自动维护,无需手动赋值;- 并发检测的
RowVersion字段为byte[]类型,EF Core会自动处理其值的比较逻辑。
内容的提问来源于stack exchange,提问作者emad.b
相关产品推荐
相关产品推荐

