实体框架事务锁定
Posted
技术标签:
【中文标题】实体框架事务锁定【英文标题】:Entity Framework Transaction Locking 【发布时间】:2017-01-06 02:21:53 【问题描述】:我在我的 web 应用程序中看到一些奇怪的竞争条件,我怀疑这可能是由于实体框架以意想不到的方式处理读取锁。当我的应用程序向任何页面发出请求时,我会自动加载帐户模型,然后在请求的生命周期内将其存储在我的 DbContext 中。一些网页需要锁定帐户数据库行,这样我就可以安全地进行一些其他操作而无需竞争条件。这就是我现在的做法......
//... code that begins the request and loads the account into context.
// Some pages may run code that looks something like this.
using(var tran = existingCtx.Database.BeginTransaction(IsolationLevel.RepeatableRead))
// Lock customer.
var act = ctx.Accounts.Find(purchaseFor.ID);
if (act == null)
throw new RecordNotFoundException("Unable to find specified customer.");
DoStuffRelyingOnLock();
Commit();
调用是否会找到 Find(purchaseFor.ID) LOCK 数据库中的帐户行,即使它已经加载到上下文中?
【问题讨论】:
【参考方案1】:调用是否会找到 Find(purchaseFor.ID) LOCK 数据库中的帐户行,即使它已经加载到上下文中?
不,不会。如果上下文已经加载了该实体,它甚至不会与数据库对话。
查看documentation 对Find
方法如何工作的说明(重点是我的):
使用主键查找实体
DbSet
上的Find
方法使用主键值来尝试查找上下文跟踪的实体。 如果在上下文中未找到实体,则将向数据库发送查询以查找那里的实体。 如果在上下文中未找到实体,则返回 Null或在数据库中。只有在上下文中找不到具有给定键的实体时才会往返于数据库。
Find
与使用查询有两个显着不同:Find
将返回处于Added
状态的实体。也就是说,Find
将返回已添加到上下文但尚未保存到数据库的实体。
因此,如果您想确保始终出于锁定目的对数据库进行查询,请避免使用Find
,而是使用Where
LINQ 方法。
【讨论】:
【参考方案2】:如果您要显式处理事务,最好让您的代码要么 提交 或 回滚。 MSDN documentation 表示需要显式回滚,但 using 语句将触发 Dispose() 方法,该方法反过来将回滚任何打开的事务。为清楚起见,尤其是当代码趋于变得更复杂时,最好显式处理回滚。
这个OpenStack question在接受的答案中有更详尽的解释。
【讨论】:
没有隐式回滚吗?如果不是,那么实现IDisposable
的事务有什么意义?
查看 mdsn 文档 link 以查看 EF 上下文中的 using 语句和正确的事务处理。
是的,你的建议是好的做法。但是说 OP 的代码会让交易保持打开状态是完全错误的。事务实例将在using
块的末尾处理。这包括如果到那时事务仍然打开,则调用回滚。
你是对的@sstan,我从上面提供的链接中关闭了 MS 文档:“你负责提交或回滚事务并在其上调用 Dispose()至于关闭和处理数据库连接。”他们立即给出了一个例子,他们在 catch 中有一个明确的回滚。我做了更多的挖掘,发现他们的回滚是多余的,因为他们确实有由使用语句触发的 Dispose() 覆盖的回滚。以上是关于实体框架事务锁定的主要内容,如果未能解决你的问题,请参考以下文章