如何使用entity frameworklocking读取的表格?
我有一个SQL Server(2012),我使用entity framework(4.1)访问。 在数据库中,我有一个名为URL的表格,一个独立的进程提供新的URL。 URL表中的条目可以处于“新build”,“正在处理”或“已处理”状态。
我需要从不同的计算机访问URL表格,检查状态为“New”的URL条目,取第一个,并将其标记为“In Process”。
var newUrl = dbEntity.URLs.FirstOrDefault(url => url.StatusID == (int) URLStatus.New); if(newUrl != null) { newUrl.StatusID = (int) URLStatus.InProcess; dbEntity.SaveChanges(); } //Process the URL
由于查询和更新不是primefaces的,我可以让两台不同的计算机读取和更新数据库中相同的URL条目。
有没有办法让select然后更新序列primefaces,以避免这种冲突?
这个答案不妨碍同时读取! 这不是这个问题的正确答案!
作者:我错了。 我不能删除这个答案,因为这是被接受的答案。 @吉拉德,如果你不接受我的回答,我会删除它。
尝试这个:
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })) { var newUrl = dbEntity.URLs.FirstOrDefault(url => url.StatusID == (int) URLStatus.New); if(newUrl != null) { newUrl.StatusID = (int) URLStatus.InProcess; dbEntity.SaveChanges(); } scope.Complete(); }
IsolationLevel.RepeatableRead
将对所read
所有行应用锁,以使Thread 2
无法从Table A
读取(如果Table A
已被Thread 1
读取且Thread 1
未完成事务)。 (根据评论似乎并非如此)
只是关于TransactionScopes的一个附注:
当你用一个TransactionScope
包围你的代码时,你并没有创build任何事务,你只是创build一个可能需要事务的范围。 当你读取数据时,Entity Framework会创build一个事务,但现在不会再有了,因为现在由于TransactionScope
缘故,你负责事务。 即:您将确定IsolationLevel并且您负责提交或回滚事务。 默认情况下,隔离级别将在写入时应用locking。 也就是说,如果我写了一个URL,它将lockingURLs表来读写。 为了在读取时应用locking,请使用IsolationLevel.RepeatableRead
。
如果你在TransactionScope
创build更多的TransactionScope
,它将被提升为分布式事务,但这不在我的答案范围之内。
我只能通过手动向表发出locking语句来实现这一点。 这是一个完整的表锁,所以要小心! 在我的情况下,创build一个队列,我不希望多个进程触摸一次是有用的。
using (Entities entities = new Entities()) using (TransactionScope scope = new TransactionScope()) { //Lock the table during this transaction entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)"); //Do your work with the locked table here... //Complete the scope here to commit, otherwise it will rollback //The table lock will be released after we exit the TransactionScope block scope.Complete(); }
更新 – 在entity framework6中,特别是使用async
/ await
代码,您需要以不同的方式处理事务。 经过一些转换之后,这对我们来说是一场崩溃
using (Entities entities = new Entities()) using (DbContextTransaction scope = entities.Database.BeginTransaction()) { //Lock the table during this transaction entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)"); //Do your work with the locked table here... //Complete the scope here to commit, otherwise it will rollback //The table lock will be released after we exit the TransactionScope block scope.Commit(); }
我不能在Andre的答案中添加注释,但是我担心这个注释“IsolationLevel.RepeatableRead将对所有读取的行应用一个锁,以致于如果读取了表A,则线程2无法从表A读取通过线程1和线程1没有完成交易“。
可重复的只读说,你将持有所有的锁,直到交易结束。 当你在一个事务中使用这个隔离级别并读取一行(比如说最大值)时,会发出一个“共享”锁,并将一直持续到事务完成。 这个共享锁将阻止另一个线程更新该行(更新会尝试对该行应用一个Exclusive锁并且被现有的共享锁阻止),但是它将允许另一个线程读取该值(第二个线程将把另一个共享锁 – 这是允许的(这就是为什么他们被称为共享锁))。 所以为了使上面的语句正确,需要说:“IsolationLevel.RepeatableRead将对所有读取的行应用一个锁,以致于如果表1已被线程1读取,则线程2不能更新表A;线程1没有完成交易。“
对于原始问题,您需要使用可重复读取隔离级别,并将锁升级为独占锁,以防止两个进程读取和更新相同的值。 所有的解决scheme都涉及将EF映射到自定义SQL(因为lockingtypes不是内置于EF中)。 你可以使用joolll答案,或者你可以使用一个更新与输出子句来locking行(更新语句总是获得排他锁,并在2008年或以上可以返回结果集)。
@jocull提供的答案很好。 我提供这个调整:
而不是这个:
"SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)"
做这个:
"SELECT TOP 0 NULL FROM MyTable WITH (TABLOCKX)"
这是更通用的。 您可以创build一个简单的表名作为参数的帮助器方法。 不需要知道数据(又名任何列名称),并且不需要实际检索pipe道中的logging(又名TOP 1
)