C# 在实体框架查询中使用TransactionScope是个好主意吗?

C# 在实体框架查询中使用TransactionScope是个好主意吗?,c#,entity-framework,design-patterns,C#,Entity Framework,Design Patterns,我一直在阅读微软模式与实践小组()的一篇文档 在第3章中,作者讨论了如何使用实体框架从数据库中加载实体。下面是他们的一些示例代码: using (var context = new PersonContext()) { Person person = null; using (var transactionScope = this.GetTransactionScope()) { person = context.Persons

我一直在阅读微软模式与实践小组()的一篇文档

在第3章中,作者讨论了如何使用实体框架从数据库中加载实体。下面是他们的一些示例代码:

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声明:

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

大胆的强调是我的

我的问题是:当使用实体框架时,如上所示的TransactionScope的使用是否过度,特别是对于数据读取

(我在发帖后意识到,这个问题的答案可能有点基于观点,对此我深表歉意。)

这要看情况而定。
如果您想要脏读、可序列化读等,而不是默认隔离级别,那么您需要将查询包装在事务范围内

这个问题有点没有定论。但事实证明,这可能有助于人们了解肮脏的阅读。隔离和EF。 你已经读过了,我们有一个明确的问题

一般来说,我会说让EF管理范围。 初学者不考虑未提交的读,这是EF的一个很好的默认值。 您可能有想要控制范围的正当理由。 或者需要使用现有事务。但仍供专家使用

现在才是真正的问题。
在隔离周围加入此类防御性编程是否是一种良好的做法。

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

Oracle和SQL server具有默认的已提交读取。我预计其他数据库也会如此。因此,我得出结论,最有可能是不必要的保护增加了复杂性。

我不会将它添加到我的代码中。

为了保持数据完整性,有时使用显式定义的多语句事务非常有用,这样,如果事务的任何部分失败,它都会回滚。关于是否使用交易的决定不应轻易作出。显式事务,尤其是分布式事务,对性能和系统的死锁造成了巨大的损失,并可能导致事务处于打开状态,从而锁定受影响的表并阻止所有其他事务。此外,代码的开发和维护要困难得多


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

你读过下面的链接吗?

如果使用saveChanges的正常行为,而不将其作为transactionscope的开始部分,则无法将事务扩展到数据库边界之外。使用上面引用中的事务作用域使消息队列成为事务的一部分。因此,如果由于某种原因消息发送失败,事务也会失败


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

使用此
isolationLevel
,如果事务只包含读取操作,则此代码是多余的。但是,如果
isolationLevel
readuncommitted
,则此代码将允许您读取脏行(尚未提交)。如果事务范围不符合业务目的,则应避免使用它。这是为了避免死锁和性能问题。EF并没有说它在SaveChanges之外有默认事务作用域,它是否在创建上下文时创建TransactionScope?