Entity Framework .NET Core未抛出DbUpdateConcurrencyException问题排查
问题描述
我有一个基于SQL Server数据库构建EF数据集的Windows Forms程序。加载表单显示StudentsTable的所有当前值后,立即使用SSMS修改底层SQL Server表中某学生的GPA,再通过表单修改该学生的GPA时,程序接受更改但未抛出DbUpdateConcurrencyException。
模型类定义
public partial class StudentsTable { public int StudentId { get; set; } public string Fname { get; set; } = null!; public string Lname { get; set; } = null!; public decimal Gpa { get; set; } public int MajorId { get; set; } public byte[] Updated { get; set; } = null!; public virtual MajorTable Major { get; set; } = null!; }
DbContext配置
public partial class StudentDbContext : DbContext { public StudentDbContext() { } public StudentDbContext(DbContextOptions<StudentDbContext> options) : base(options) { } public virtual DbSet<MajorTable> MajorTables { get; set; } public virtual DbSet<StudentsTable> StudentsTables { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer("xxxxxxxxxxxxx;Database=StudentDB;Trusted_Connection=True; TrustServerCertificate=True"); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<MajorTable>(entity => { entity.HasKey(e => e.MajorId); entity.ToTable("MajorTable"); entity.Property(e => e.MajorId).HasColumnName("MajorID"); entity.Property(e => e.Major).HasMaxLength(50); }); modelBuilder.Entity<StudentsTable>(entity => { entity.HasKey(e => e.StudentId); entity.ToTable("StudentsTable"); entity.Property(e => e.StudentId).HasColumnName("StudentID"); entity.Property(e => e.Fname).HasMaxLength(50); entity.Property(e => e.Gpa) .HasColumnType("numeric(18, 0)") .HasColumnName("GPA"); entity.Property(e => e.Lname).HasMaxLength(50); entity.Property(e => e.MajorId).HasColumnName("MajorID"); entity.Property(e => e.Updated) .IsRowVersion() .IsConcurrencyToken(); entity.HasOne(d => d.Major).WithMany(p => p.StudentsTables) .HasForeignKey(d => d.MajorId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FK_StudentsTable_MajorTable"); }); OnModelCreatingPartial(modelBuilder); } partial void OnModelCreatingPartial(ModelBuilder modelBuilder); }
数据库的时间戳列对应模型中的Updated属性,已标记为IsRowVersion和IsConcurrencyToken。
更新操作代码
int newGPA; //Variable to hold textBox input if (int.TryParse(textBox1.Text, out newGPA)) { var selected = dataGridView1.CurrentRow; int selectedStudentId = (int)selected.Cells["StudentId"].Value; var selectedEvent = (from eachStudent in myContext.StudentsTables where eachStudent.StudentId == selectedStudentId select eachStudent).Single(); selectedEvent.Gpa = newGPA; //Edit one object try { myContext.SaveChanges(); // Save Changes } catch (Exception) { throw; } }
请问是否需要在C#代码或SQL Server数据库中进行其他设置,以触发并发异常?
解决方案
1. 绕过Change Tracker获取最新实体
你的myContext大概率是表单加载时创建的长期实例,EF的Change Tracker会缓存第一次加载的实体(包括旧的Updated行版本值)。后续查询时,EF会直接返回缓存实例,不会从数据库拉取最新的行版本,导致并发检测失效。
解决方式:
- 查询时使用
AsNoTracking()绕过缓存:
var selectedEvent = myContext.StudentsTables .AsNoTracking() .Single(e => e.StudentId == selectedStudentId);
- 或者在修改前强制刷新实体:
myContext.Entry(selectedEvent).Reload();
2. 确认数据库行版本列类型
确保SQL Server中Updated列是rowversion类型(而非普通varbinary),定义如下:
Updated rowversion NOT NULL
该类型会在每次行更新时自动生成新值,是EF检测并发的核心依据。
3. 优化DbContext生命周期
避免长期持有DbContext实例,建议在每次操作时创建短期实例并释放:
using (var myContext = new StudentDbContext()) { var selectedEvent = myContext.StudentsTables .Single(e => e.StudentId == selectedStudentId); selectedEvent.Gpa = newGPA; myContext.SaveChanges(); }
这样每次操作都会从数据库获取最新的行版本,自然能检测到并发修改。
4. 明确捕获并发异常
调整异常捕获逻辑,直接监听DbUpdateConcurrencyException,避免上层代码吞掉异常:
try { myContext.SaveChanges(); } catch (DbUpdateConcurrencyException) { MessageBox.Show("该学生信息已被修改,请刷新后重试"); // 可选:刷新实体到最新状态 myContext.Entry(selectedEvent).Reload(); } catch (Exception ex) { // 处理其他异常 }
内容的提问来源于stack exchange,提问作者kurtfriedrich
相关产品推荐
相关产品推荐

