【发布时间】:2013-07-15 18:52:19
【问题描述】:
我想设置实体框架,以便 SavesChanges() 不会用 BEGIN 和 COMMIT 包装其生成的所有 SQL。我从一个相关的SO question 那里得到了这个建议:
using( var transation = new TransactionScope(TransactionScopeOption.Suppress) )
{
ObjectContext.SaveChanges();
}
但这对我不起作用。我们正在使用 MYSQL 连接器,所以我确实通过逐步浏览源代码进行了一些挖掘,我发现 EntityFramework 是要求创建新事务的那个,而不是连接器的错误,请参见以下内容堆栈跟踪。
接下来我开始翻阅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 连接器的唯一解决方案?
谢谢!
【问题讨论】:
-
无论您尝试做什么,都可能做错了。禁用 EF 的内部事务将使 EF 内部代码在操作后处于未知状态。只是不要这样做。而是描述您要解决的问题。最好通过为每个工作单元声明一个新的上下文来使用 EF。一个工作单元应该由最少数量的操作组成,如果回滚,这些操作仍然会使业务数据保持逻辑正确的状态。创建订单可以是独立于向订单添加行项目的工作单元。这最适用于 MVVM 架构。
-
@Kurian,你对未知状态是正确的,所以我不会对所有 EF 连接都这样做。但是,我们有很多事件,每个事件都执行一次插入,并且每个事件都有它们不需要的事务的额外性能开销。
-
我可以盲目地声明,数据库操作不是您所做工作的瓶颈。这是不太可能的。事实上,每个插入语句都应该在一个事务中,并在它正在读取和写入的任何表上进行适当的锁定等。DBMS 就是为此而设计的。您执行的每条语句都会发生各种隐式事务和锁定。 EF 交易可以忽略不计。如果缺乏性能,则说明您存在代码质量问题。
-
@Kurian,我怀疑你是对的。但是,我们有一个跟踪工具,显示 BEGIN 语句的时间为 5 毫秒,实际的 sql 为 15 毫秒。因此,取出BEGIN可能会使sql运行20ms,但是如果不关闭EF中的内部事务,我将无法尝试......
-
这样想:每条语句都在某种事务中执行。显式声明事务仅使您能够移动端点以覆盖多行。每个单行语句都有隐式事务和锁。延迟更有可能是客户端。 EF很重,每次操作都很慢。显式事务可能会减慢 EF 代码,但不会减慢数据库引擎。最好将一些内容移动到通过上下文执行的普通 SQL 语句中。
标签: entity-framework mysql-connector