我有一些 .NET 代码包含在一个可重复的读取事务中,如下所示:
using (
var transaction = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead },
TransactionScopeAsyncFlowOption.Enabled))
{
int theNextValue = GetNextValueFromTheDatabase();
var entity = new MyEntity
{
Id = Guid.NewGuid(),
PropertyOne = theNextValue, //An identity column
PropertyTwo = Convert.ToString(theNextValue),
PropertyThree = theNextValue,
...
};
DbSet<MyEntity> myDbSet = GetEntitySet();
myDbSet.Add(entity);
await this.databaseContext.Entities.SaveChangesAsync();
transaction.Complete();
}
第一个方法,GetNextValueFromTheDatabase
,检索存储在数据库表的列中的最大值。我正在使用可重复读取,因为我不希望两个用户读取和使用相同的值。然后,我只需在内存中创建一个 Entity
并调用 SaveChangesAsync()
将值写入数据库。
偶尔,我发现 entity.PropertyOne、entity.PropertyTwo 和 entity.PropertyThree 的值彼此不匹配。例如,entity.PropertyOne 的值为 500,但 entity.PropertyTwo 和 entity.PropertyThree 的值为 499。这怎么可能?即使代码未包含在事务中,我也希望这些值匹配(如果两个用户同时运行,则可能在实体之间重复)。
我正在使用 Entity Framework 6 和 Sql Server 2008R2。
编辑:
这是 GetNextValueFromTheDatabase
public async Task<int> GetNextValueFromTheDatabase()
{
return await myQuerable
.OrderByDescending(x => x.PropertyOne) //PropertyOne is an identity column (surprise!)
.Select(x => x.PropertyOne)
.Take(1)
.SingleAsync() + 1;
}
最佳答案
所以这个问题无法明确回答,因为 GetNextValueFromTheDatabase
没有显示。我将按照您所说的去做:
REPEATABLE READ
S 锁定您已读取的行。当您读取当前最大值时,大概是从索引中读取的,该行是 S 锁定的。现在,如果出现新的最大值,则该行不受锁定影响。这就是为什么锁不会阻止其他竞争最大值出现的原因。
如果您通过从表中读取最大值来获取最大值,则需要SERIALIZABLE
隔离。这将导致您的特定情况出现死锁。这可以通过锁定提示或重试来解决。
您还可以保留一个单独的表来存储当前的最大值。 REPEATABLE READ
在这里就足够了,因为您总是访问该表的同一行。即使 REPEATABLE READ
没有锁定提示,您也会在这里看到死锁。
重试是解决死锁的有效方法。
关于c# - 具有可重复读取隔离的事务中的数据是否可能发生变化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27828809/