You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:17:16