如何全局更改所有Entity Framework事务的IsolationLevel

时间:2011-02-26 11:26:22

标签: .net entity-framework

我正在为我的下一个新应用评估EF。

如何全局更改应用程序中所有EF事务的IsolationLevel? 例如:假设我想使用“Read Committed Snapshot”。

虽然当我显然需要一个TransactionScope时指定IsolationLevel是可以的(参见下面的代码),但是在TransactionScope中封装每个EF保存操作都会很难看。

 'OK
    Using tsc As New TransactionScope(TransactionScopeOption.RequiresNew, TransactionOption.ReadCommitted)
        UpdateShoppingCart
        EnqueueNewOrder
        SendConfirmationEmail
        tsc.Complete
    End Using

    'Is this really the only way to avoid Serializable?
    Using tsc As New TransactionScope(TransactionScopeOption.RequiresNew, TransactionOption.ReadCommitted)
      _ctx.SaveChanges()
      tsc.Complete
    End Using

    Class TransactionOption
        Public Shared ReadOnly ReadCommitted As New TransactionOptions() With {
            .IsolationLevel = IsolationLevel.ReadCommitted,
            .Timeout = TransactionManager.DefaultTimeout
            }
    End Class

我认为混合IsolationLevles并不是一个好主意。我错了吗?

使用Serializable和SQL Server(与Oracle相反)插入简单无辜的读取可能会导致转换锁定死锁。

来自EF FAQ: “建议您使用READ COMMITTED事务,并使用READ COMMITTED SNAPSHOT ISOLATION,如果您需要让读者不阻止编写者和编写者不阻止读者。”

我不明白为什么EF默认使用Serializable并且很难改变默认的隔离级别 - 使用SQL Server(与Oracle的多版本相比)默认为悲观的并发模型。配置选项应该很容易实现 - 或者我在这里遗漏了什么?

2 个答案:

答案 0 :(得分:9)

我几乎可以肯定默认的EF事务隔离级别是基于使用过的数据库提供程序。 SaveChanges执行此代码:

    ... 
    try
    {
        this.EnsureConnection();
        flag = true;
        Transaction current = Transaction.Current;
        bool flag2 = false;
        if (connection.CurrentTransaction == null)
        {
            flag2 = null == this._lastTransaction;
        }
        using (DbTransaction transaction = null)
        {
            if (flag2)
            {
                transaction = connection.BeginTransaction();
            }
            objectStateEntriesCount = this._adapter.Update(this.ObjectStateManager);
            if (transaction != null)
            {
                transaction.Commit();
            }
        }
    }
    ...

如您所见,BeginTransaction在未指定IsolationLevel的情况下被调用。在幕后,它使用IsolationLevel.Unspecified创建提供商特定的交易。未指定的隔离级别应导致数据库服务器/驱动程序的默认隔离级别。在SQL Server中,默认隔离级别为READ COMMITED,所以我希望它应该使用它,但我还没有测试过它。

如果您想全局更改隔离级别,可以覆盖从SaveChanges派生的类中的ObjectContext,并在自定义base.SaveChanges()中包装TransactionScope

答案 1 :(得分:0)

VB中可能存在限制但在C#中你会这样做:

 TransactionOptions transactionOptions = GetTransactionOptions();
 new TransactionScope(TransactionScopeOption.RequiresNew, transactionOptions) 

请注意,第二个参数是transactionOptions,可以按如下方式创建:

 public TransactionOptions GetTransactionOptions()
    {
        TransactionOptions transactionOptions = new TransactionOptions();
        transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
        return transactionOptions;
    }

通过将上述代码放在Factory方法中,并在需要事务选项时调用该工厂方法,您可以在一个位置更改解决方案中的所有事务范围。