我保存了用于帮助最小化并发问题的代码。它的结构类似于:
// HasConcurrencyIssues opens a new EntityContext and gets the actual current
// details and then compares it to what was loaded for the form
if (this.HasConcurrencyIssues()) {
// can't save, exit out of save code
return;
}
using (EntityContext context = new EntityContext()) {
// shared locks to avoid dirty reads - should I be using this?
IsolationLevel level = IsolationLevel.ReadCommitted;
using (DbContextTransaction trans == context.Database.BeginTransaction(level)) {
try {
// here is where I save stuff using the context by adding
// items to the DbSets
this.SaveStuff(context);
// execute the generated SQL commands before secondary check because
// this could take some time
context.SaveChanges();
// check again in case something happened after executing SaveChanges()
// ** gets blocked here **
if (this.HasConcurrencyIssues()) {
// can't save, rollback and exit out of save code
trans.Rollback();
return;
}
trans.Commit();
} catch (Exception ex) {
// handle error
trans.Rollback();
}
}
}
我使用此结构来帮助最小化并发问题。我遇到的问题是我并不真正理解ReadCommitted
和并发如何协同工作。我注意到在第二次检查并发问题时它被阻止了,因为它无法读取当前记录(我假设这与我选择的IsolationLevel
有关)。我担心如果我删除那个二次检查,它可能会导致我不想要的幻像数据。
应用程序正在扩展,需要能够处理大约200个可能想要编辑同一个对象并保存的用户。我基本上想要一个硬停止来检查对象是否已经改变,如果有,则阻止保存。
如何构建我的保存代码以最小化并发问题?
修改
我意识到它被锁定了,因为支票是在SaveChanges
电话之后。如果我在SaveChanges
调用之前进行二次检查它会起作用,但是在保存时事务是否可能完成,这会导致并发问题出现,但不会?
相关系统架构:
Entity Framework 6
SQL Server 2012
答案 0 :(得分:0)
我认为这是一种更简单的方法:
using (var ts = new TransactionScope())
{
if (this.HasConcurrencyIssues()) {
// can't save, exit out of save code
return;
}
using (EntityContext context = new EntityContext()) {
this.SaveStuff(context);
context.SaveChanges();
}
ts.Complete();
}
因为当您从数据库中读取信息时,事务范围已经开始,所以在您完成保存之前,不允许其他任何线程更改该信息。
同样,如果另一个线程已经读取信息以确定是否存在任何并发问题,则对HasConcurrencyIssues()的调用将不会返回,直到它们完成保存更改为止。
如果事务范围尚未完成,则事务范围将在using
块的末尾自动回滚,因此不需要额外的try / catch逻辑。
答案 1 :(得分:0)
对同一数据处理两个事务是一件麻烦事。您将获得各种阻止行为,通常取决于查询计划。在测试期间,这可能会起作用,然后在负载下中断您将创建分布式死锁(涉及多个连接)。 SQL Server无法破坏它们。您基本上会锁定一些数据并在30秒超时期间暂停执行。可伸缩性杀手。
使用单个上下文,连接和事务。锁定正确。
要么更好地理解SQL Server中的锁定并应用正确的隔离级别,也许应该使用悲观的锁定提示。或者,使用始终有效的解决方案:SERIALIZABLE
。如果只有一个事务同时运行,则此隔离级别可以保证结果。通过重试事务来处理死锁。
SERIALIZABLE
往往会导致更多阻塞和死锁。根据工作量的不同,这可能根本不是问题,也可能是致命的。
为读者调查SNAPSHOT
隔离。它消除了对数据的所有锁定,从而实现了完美的数据一致性。
这实际上是我可以给出的所有建议,而没有关于具体查询和锁定的信息。
答案 2 :(得分:0)