【问题标题】:Is using TransactionScope in Entity Framework queries a good idea?在实体框架查询中使用 TransactionScope 是个好主意吗?
【发布时间】:2015-03-05 14:50:57
【问题描述】:

我一直在阅读 Microsoft 模式与实践小组 (Data Access for Highly-Scalable Solutions: Using SQL, NoSQL, and Polyglot Persistence) 的文档。

在第 3 章的 "Retrieving Data from the SQL Server Database" 部分,作者讨论了使用实体框架从数据库加载实体。这是他们的一些示例代码:

using (var context = new PersonContext())
{
    Person person = null;

    using (var transactionScope = this.GetTransactionScope())
    {
        person = context.Persons
            .Include(p => p.Addresses)
            .Include(p => p.CreditCards)
            .Include(p => p.EmailAddresses)
            .Include(p => p.Password)
            .SingleOrDefault(p => p.BusinessEntityId == personId);

        transactionScope.Complete();
    }

    // etc...
}

注意通过GetTransactionScope 方法使用自定义事务范围,在它们的基本上下文类中实现,如下所示:

public abstract class BaseRepository
{
    private static TransactionOptions transactionOptions = new TransactionOptions()
    {
        IsolationLevel = IsolationLevel.ReadCommitted
    };

    protected virtual TransactionScope GetTransactionScope()
    {
        return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
    }
}

MSDN 上的Working with Transactions (EF6 Onwards) 声明:

在所有版本的 Entity Framework 中,每当您执行 SaveChanges() 以在数据库上插入、更新或删除时,框架都会将该操作包装在事务中 [...] 事务的隔离级别是任何隔离级别数据库提供程序考虑其默认设置。例如,默认情况下,在 SQL Server 上,这是 READ COMMITTED。 实体框架不会将查询包装在事务中。此默认功能适用于很多用户

大胆的强调是我的。

我的问题:使用 Entity Framework 时,使用如上所示的 TransactionScope 是否过度,特别是对于数据读取?

(发布后我意识到这个问题的答案可能有点基于意见,对此感到抱歉。)

【问题讨论】:

  • 有了这个isolationLevel,如果事务只包含读操作,这个代码就有点过分了。但是如果isolationLevelReadUncommited,则这段代码将允许您读取脏行(尚未提交)。
  • 如果它不能用于商业目的,你应该避免使用事务范围。这是为了避免死锁和性能问题。

标签: c# entity-framework design-patterns


【解决方案1】:

这个问题有点开放式。但可能对学习脏读的人有用。隔离和 EF。 你已经阅读了EF Transaction Scope ef6 ,我们有一个明确的问题。

通常我会说让 EF 管理范围。 初学者不考虑未提交的读取,它是 EF 的一个很好的默认值。

您可能有正当理由想要控制范围。 或者需要使用现有的事务。但仍供专家使用。

所以现在才是真正的问题。
围绕隔离包含这种防御性编程是否是一种好习惯。

我的看法:
除非它不会使代码更难维护,更难重用。

Oracle 和 SQL 服务器默认读取已提交。我希望其他数据库也是如此。因此,我会得出结论,很可能是不必要的保护增加了复杂性。
我不会将它添加到我的代码中。

【讨论】:

  • EF 并没有说它在 SaveChanges 之外具有默认事务范围,它是否在创建上下文时创建 TransactionScope?
【解决方案2】:

这取决于。 如果您想要脏读、可序列化读取等默认隔离级别以外的任何内容,那么您需要将查询包装在事务范围内。

【讨论】:

    【解决方案3】:

    为了保持数据完整性,有时使用显式定义的多语句事务很有用,这样如果事务的任何部分失败,它都会被回滚。不应轻易做出是否使用交易的决定。显式事务,尤其是分布式事务,对性能有很大影响,并且会使系统陷入死锁,并且有可能使事务保持打开状态,从而锁定受影响的表并阻塞所有其他事务。此外,代码的开发和维护难度要大得多。

    根据我的经验,我发现在 C# 代码中使用事务范围比在 SQL 中使用显式事务更复杂。因此,对于将从事务中受益的操作,以及任何足够复杂的查询,我建议创建一个从 ORM 调用的存储过程。

    【讨论】:

      【解决方案4】:

      您是否阅读了以下链接? https://msdn.microsoft.com/en-us/library/vstudio/bb738523%28v=vs.100%29.aspx

      如果您要使用 saveChanges 的正常行为而不将它作为事务范围的一部分,您将无法将事务扩展到数据库边界之外。使用上面参考中的事务范围使消息队列成为事务的一部分。所以如果由于某种原因消息发送失败,交易也会失败。

      当然,您可能会认为这是一个更复杂的场景。在不需要如此复杂逻辑的简单应用程序中,使用普通的 saveChanges api 可能更安全。

      【讨论】:

        猜你喜欢
        • 2014-03-17
        • 1970-01-01
        • 1970-01-01
        • 2013-07-22
        • 2012-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多