Gil*_*Gat 58 c# sql-server entity-framework multiprocessing
我有一个SQL Server(2012),我使用Entity Framework(4.1)访问.在数据库中,我有一个名为URL的表,一个独立的进程在其中提供新的URL.URL表中的条目可以处于"新建","处理中"或"已处理"状态.
我需要从不同的计算机访问URL表,检查状态为"新"的URL条目,取第一个并将其标记为"正在处理".
var newUrl = dbEntity.URLs.FirstOrDefault(url => url.StatusID == (int) URLStatus.New);
if(newUrl != null)
{
    newUrl.StatusID = (int) URLStatus.InProcess;
    dbEntity.SaveChanges();
}
//Process the URL
由于查询和更新不是原子的,我可以读取两个不同的计算机并更新数据库中的相同URL条目.
有没有办法让select-then-update序列原子化以避免这种冲突?
joc*_*ull 51
我只能通过手动向表发出锁定语句来实现这一点.这样就完成了一个表锁,所以要小心!在我的情况下,它对于创建一个我不希望一次触摸多个进程的队列很有用.
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();
}
更新 - 在实体框架6中,特别是使用async/ awaitcode,您需要以不同方式处理事务.经过一些转换后,这对我们来说是崩溃的.
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();
}
小智 17
我不能对Andre的答案添加注释,但我担心这个注释"IsolationLevel.RepeatableRead将对所有读取的行应用锁定,如果表A已被读取,则线程2无法从表A读取线程1和线程1没有完成交易."
可重复读只表示您将保留所有锁,直到事务结束.当您在事务中使用此隔离级别并读取一行(例如最大值)时,将发出"共享"锁定并将一直保持到事务完成.这个共享锁将阻止另一个线程更新该行(更新将尝试对该行应用Exclusive锁并且将被现有共享锁阻止),但它将允许另一个线程读取该值(第二个线程)将在行上放置另一个共享锁 - 这是允许的(这就是为什么它们被称为共享锁)).因此,为了使上述语句正确,需要说"IsolationLevel.RepeatableRead将对所有读取的行应用锁定,以便线程2无法更新 表A如果表1已被线程1读取且线程1未完成事务."
对于原始问题,您需要使用可重复的读隔离级别并将锁升级为Exclusive锁,以防止两个进程读取和更新相同的值.所有解决方案都涉及将EF映射到自定义SQL(因为升级锁定类型不是内置到EF中).您可以使用jocull answer或者您可以使用带有output子句的更新来锁定行(update语句总是获得Exclusive锁,而在2008或更高版本中可以返回结果集).
Tim*_*uri 14
@jocull提供的答案很棒.我提供这个调整:
而不是这个:
"SELECT TOP 1 KeyColumn FROM MyTable WITH (TABLOCKX, HOLDLOCK)"
做这个:
"SELECT TOP 0 NULL FROM MyTable WITH (TABLOCKX)"
这更通用.您可以创建一个简单地将表名作为参数的辅助方法.无需知道数据(也就是任何列名),也无需实际检索管道中的记录(又名TOP 1)
| 归档时间: | 
 | 
| 查看次数: | 47212 次 | 
| 最近记录: |