【问题标题】:How to globally change IsolationLevel of all Entity Framework transactions如何全局更改所有实体框架事务的 IsolationLevel
【发布时间】:2011-02-26 22:41:50
【问题描述】:

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

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

虽然当我明确需要 TransactionScope 时指定 IsolationLevel 是可以的(请参阅下面的代码),但必须将每个 EF 保存操作封装在 TransactionScope 中会很丑。

 '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 常见问题解答: “建议你使用 READ COMMITTED 事务,如果你需要 reader 不阻塞 writer 和 writer 不阻塞 reader,则使用 READ COMMITTED SNAPSHOT ISOLATION。”

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

【问题讨论】:

  • 您在哪里发现 EF 默认为 Serializable?

标签: .net entity-framework


【解决方案1】:

我几乎可以肯定默认的 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,所以我希望它应该使用它,但我还没有测试过。

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

【讨论】:

  • 你是对的,我的假设是错误的。也许我在测试时偶然发现了以下问题:connect.microsoft.com/SQLServer/feedback/details/243527/… 我通过首先执行 SqlConnection.ClearAllPools() 并使用 SQLProfiler 检查来验证 EF 对 SQL Server 的默认设置是“已提交读”。
【解决方案2】:

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

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

注意第二个参数是transactionOptions,可以这样创建:

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

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

【讨论】:

  • 这正是我在我的问题中发布的代码中所做的:-) 我正在寻找一种方法,不必将每个 .SaveChanges 与 TransactionScope 一起更改 EF 的默认可序列化为已提交读快照.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多