没有事务的Entity Framework 5

4
我希望设置实体框架,使SavesChanges()不会用BEGIN和COMMIT包装其生成的所有SQL。我在相关的SO问题中发现了这个建议: (链接)
using( var transation = new TransactionScope(TransactionScopeOption.Suppress) )
{
    ObjectContext.SaveChanges();
}

但这对我没用。我们正在使用MYSQL连接器,所以我通过分析源代码找到了实体框架(EntityFramework)是要求创建一个新事务,而不是连接器的问题,请看下面的堆栈跟踪。 MySQL stack trace

接下来我开始查看EF的源代码(EF 6是开源的,反编译后相关代码看起来相同)

        var needLocalTransaction = false;
        var connection = (EntityConnection)Connection;
        if (connection.CurrentTransaction == null
            && !connection.EnlistedInUserTransaction
            && _lastTransaction == null)
        {
            needLocalTransaction = startLocalTransaction;
        }

        ...


        if (needLocalTransaction)
        {
            localTransaction = connection.BeginTransaction();
        }

好的,看起来如果一个事务不存在,它会自己创建一个。进一步了解EF中当前事务如何设置,我找到了EF设置连接的代码。

            var currentTransaction = Transaction.Current;

            EnsureContextIsEnlistedInCurrentTransaction(
                currentTransaction,
                () =>
                {
                    Connection.Open();
                    _openedConnection = true;
                    _connectionRequestCount++;
                    return true;
                },
                false);

似乎只有在这里“TransactionScopeOption.Suppress”才能发挥作用,但它所做的只是将环境事务(Transaction.Current)设置为null。这迫使EF无法看到事务,并执行相反的操作,即创建一个新事务。

有没有人在EF 5中关闭事务并取得成功,或者我的唯一解决方案是黑客和构建自己版本的sql连接器?

谢谢!


2
无论你想做什么,你可能都做错了。禁用EF的内部事务会在操作后使EF内部代码处于未知状态。不要这样做。相反,描述你试图解决的问题。最好为每个工作单元声明一个新的上下文来使用EF。如果回滚,工作单元应该包含最少数量的操作,以使业务数据仍处于逻辑上正确的状态。创建订单可以是与添加订单行项目不同的工作单元。这与MVVM架构最搭配。 - Monstieur
@Kurian,你说的未知状态是正确的,所以我不会为所有EF连接都这样做。然而,我们有很多事件每次只插入一个,每个事件都有一个不需要的事务附加性能开销。 - Vdex
1
我可以毫不犹豫地说,无论你在做什么,数据库操作都不是瓶颈。这种情况非常罕见。事实上,每个插入语句都应该在一个事务中,并且对它正在读取和写入的任何表进行适当的锁定等操作。DBMS 就是为此而设计的。对于你执行的每个语句,都会发生各种隐式事务和锁定。EF 事务是微不足道的。如果性能不足,那么你就有代码质量问题。 - Monstieur
@Kurian,我认为你是对的。但是我们有一个跟踪工具,显示BEGIN语句大约需要5毫秒,然后实际的SQL需要15毫秒。因此,可能去掉BEGIN会使SQL运行20毫秒,但是如果不关闭EF中的内部事务,我无法尝试这个方法... - Vdex
2
这样想吧:每个语句都在某种事务中执行。显式声明事务只是让你能够将终点移动到覆盖多行的位置。每个单行语句都有隐式事务和锁定。延迟更可能是客户端的问题。EF很重,每个操作都很慢。显式事务可能会减慢EF代码的速度,但不会影响数据库引擎。最好将一些东西移到普通SQL语句中,通过上下文执行。 - Monstieur
显示剩余2条评论
1个回答

0

EF5被硬编码为将INSERTS和UPDATES包装到事务中。在几乎所有情况下,这是批量写入多个实体时所需的行为。

如果您想更改它,您将不得不为自己编写新代码,将其编译为适当的DLL,并引用新代码。为了避免一个非常重要的安全功能而进行大量工作。我不建议这样做。

问题是为什么您如此想要禁用事务?

它会导致性能问题吗?您是否认为事务的额外安全性对业务流程有害?

听起来您并不关心写入是否丢失,幻读等问题(我假设您想完全跳过事务),这是一个坏主意。

如果不是这种情况(我希望不是),并且您担心性能问题,也许您的事务级别隔离对您想要的内容太高了?您可能需要将其他调用包装在设置了更低(更不安全,但更少锁定以获得更好的性能)隔离级别的事务中,例如READ UNCOMMITTED?


谢谢Jon,这是一段时间以前的事了。从记忆中来看,我的问题是写入性能慢,而且我误解了跟踪工具归因于BEGIN和END语句的时间。如果我没记错的话,我们最终选择了一个微型ORM解决方案,它允许我们编写UPDATE WHERE语句之类的内容,这大大提高了性能。 - Vdex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接