C# 数据是否可能在具有可重复读取隔离的事务中更改?

C# 数据是否可能在具有可重复读取隔离的事务中更改?,c#,sql-server,entity-framework,transactions,transactionscope,C#,Sql Server,Entity Framework,Transactions,Transactionscope,我将一些.NET代码包装在一个可重复读取事务中,如下所示: using ( var transaction = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead },

我将一些.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();
            }

因此,这个问题无法得到明确回答,因为未显示数据库中的
getnextvalue
。我要偏离你说的,它的作用是:

SQL Server中的可重复读取会锁定已读取的行。当您读取当前最大值(可能是从索引读取)时,该行被S锁定。现在,如果出现新的最大值,则该行不受锁定的影响。这就是为什么锁不能阻止其他相互竞争的最大值出现

如果通过从表中读取最大值来获得最大值,则需要
SERIALIZABLE
隔离。在您的特定情况下,这将导致死锁。这可以通过锁定提示或重试来解决

您还可以保留一个单独的表来存储当前的最大值<代码>可重复读取在这里就足够了,因为您总是访问该表的同一行。即使使用
REPEATABLE READ
而没有锁定提示,您也会在这里看到死锁


重试是解决死锁的有效方法。

我认为您基本上正在体验幻影读取

考虑两个事务T1、T2,它们被安排执行,如下所示。问题是,在T1的第一次读取中,您不会得到从事务T2插入的值(X)。在第二次执行时,您将在select语句中获得值(X)。这就是可重复阅读的可怕本质。如果从表中读取某些行,则不会阻止在整个表中插入。它只锁定现有行

T1                                     T2

SELECT A.X FROM WeirdTable

                                       INSERT INTO WeirdTable TABLE (A) VALUES (X)
SELECT A.X FROM WeirdTable

更新


这个答案似乎与这个具体问题无关。它与可重复读取隔离级别有关,与此问题的关键字相匹配,并且没有明显错误,因此我将把它留在这里。

我终于找到了答案。如中所述,多个事务可以同时读取相同的最大值(S锁)。问题在于其中一列是
identity
列。EF允许您在插入时指定标识列的值,但忽略您指定的值。因此,identity列似乎在大多数情况下都会使用预期值进行更新,但实际上域实体中指定的值恰好与数据库内部生成的值匹配

例如,假设当前的最大值是499,事务A和事务B都是499。当事务A完成时,它成功地将500写入所有三个属性。事务B尝试将500写入所有3列。非标识列已成功更新为500,但标识列的值会自动增加到下一个可用值(不会引发错误)

一些解决方案

我使用的解决方案是在插入记录时不设置任何列的值。插入记录后,使用数据库分配的标识列的值更新其他两列

另一个选项是将列的选项更改为
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)

…这将比第一个选项执行得更好,但需要usr建议的更改以缓解锁定问题。

但再次查看查询时,我感到困惑。这些值可能是错误的,因为发生了可重复读取和幻象读取。但是他们怎么能不知道呢?如果函数只调用一次,且值存储在变量中。可能/不应该因为其他事务中发生的某些事情而真正发生匹配?不确定您的意思。您可能会获得10个和另一个事务插入11个。现在插入11。这是一个冲突。是的,您可能会得到两个相等的行,但问题是为什么PropertyOne和PropertyTwo有时在一个实体内不匹配。好的,我误解了这个问题。问题是您正在向其他地方的某个属性写入。使用显示的代码无法诊断此问题。没有人能回答这个问题。我自己也很内疚,但我看到实体框架事务中发生了一些奇怪的事情。如果真的没有其他事务修改这些行,那将很有趣。这个问题变得太奇怪了,所以简单的答案是肯定的。谢谢你的回答,但这并不能解释为什么我会在同一个表的不同列中有不同的值。
T1                                     T2

SELECT A.X FROM WeirdTable

                                       INSERT INTO WeirdTable TABLE (A) VALUES (X)
SELECT A.X FROM WeirdTable