带有从 SQL Server 数据库构建的 EF 数据集的简单 Windows 窗体程序。
如果我加载表单,该表单显示
StudentsTable
中的所有当前值,然后立即使用 SSMS 在基础 SQL Server 表中更改学生的 GPA,然后使用该表单更改学生的 GPA,它会接受更改并且不会抛出 DbUpdateConcurrencyException
。
这是 SQL Server 表:
我使用 Visual Studio
Scaffold-DbContext
命令根据我的数据库创建模型。
这是从数据库表创建的结果类
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
和 IsConcurrency
。
这是执行更新的 C# 代码:
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 数据库中进行其他设置才能“打开”并发异常?
我几乎修好了。我编辑了
StudentDbContext
文件并更改了
entity.Property(e => e.Updated)
.IsRowVersion()
.IsConcurrencyToken();
到
entity.Property(e => e.Updated)
.IsRowVersion()
.IsConcurrencyToken(true);
现在,如果我运行该程序的 2 个副本,在它们都启动并显示相同的 GPA 数字之后,如果我从 1 个副本更新 GPA,然后从第 2 个副本更新相同的值,而不是例外,第 2 个副本“赢”,但是如果我继续从其中任何一个进行更新,此后每次都会正确抛出 DbUpdateConcurrencyException。为什么第一次没有检测到,但之后就可以工作了,这是一个新的谜团。