【问题标题】:Pessimistic locking in EF code first首先在 EF 代码中进行悲观锁定
【发布时间】:2013-10-10 18:22:00
【问题描述】:

我想专门锁定表中的指定行,因此在实际事务完成之前不允许读取任何更新。为此,我在我的数据库存储库中创建了一个帮助类:

public void PessimisticMyEntityHandler(Action<IEnumerable<MyEntity>> fieldUpdater, string sql, params object[] parameters)
{
    using (var scope = new System.Transactions.TransactionScope())
    {
        fieldUpdater(DbContext.Set<MyEntity>().SqlQuery(sql, parameters));
        scope.Complete();
    }
}

这是我的测试代码。基本上我只是开始了两个任务,他们都试图用 Id '1' 锁定行。我的猜测是,在第一个任务完成其工作之前,第二个任务将无法读取(和更新)该行,但输出窗口显示它实际上可以。

Task.Factory.StartNew(() =>
{
      var dbRepo = new DatabaseRepository();
      dbRepo.PessimisticMyEntityHandler(myEntities =>
      {
                Debug.WriteLine("entered into lock1");

                /* Modify some properties considering the current ones... */
                var myEntity = myEntities.First();
                Thread.Sleep(1500);
                myEntity.MyEntityCode = "abcdefgh";
                dbRepo.Update<MyEntity>(myEntity);

                Debug.WriteLine("leaving lock1");
     }, "SELECT * FROM MyEntities WITH (UPDLOCK, HOLDLOCK) WHERE Id = @param1", new SqlParameter("param1", 1));
});

Task.Factory.StartNew(() =>
{
      Thread.Sleep(500);
      var dbRepo = new DatabaseRepository();
      dbRepo.PessimisticMyEntityHandler(myEntities =>
      {
                Debug.WriteLine("entered into lock2");

                /* Modify some properties considering the current ones... */
                var myEntity = myEntities.First();
                myEntity.MyEntityCode = "xyz";
                dbRepo.Update<MyEntity>(myEntity);

                Debug.WriteLine("leaving lock2");
     }, "SELECT * FROM MyEntities WITH (UPDLOCK, HOLDLOCK) WHERE Id = @param1", new SqlParameter("param1", 1));
});

输出窗口:

entered into lock1
entered into lock2
leaving lock2
leaving lock1

【问题讨论】:

    标签: c# sql-server asp.net-mvc entity-framework locking


    【解决方案1】:

    您的要求涉及 DBMS 中的两个主要现象,尤其是 SQL Server 中的两个主要现象:LockIsolation Level。我尽我所能在夏天解释它们。

    您询问了Pessimistic Concurrency。答案是:Entity Framework 尚不支持它。换句话说,通过 EF 的常规 API,您不能像 Oracle 通过 SELECT FOR UPDATE 那样为 SELECT 锁定表或某些行。尽管您可以通过编写本机SQL 命令来使用Exclusive 锁定选择某些行或整个表并保持此锁定直到事务结束来实现此目的。这样其他线程不仅不能更新选中的行,也不能选中它们。在您释放锁之前,它们会被阻止。这就是我在项目中所做的,虽然有点冒险,但效果很好。

    所以在夏天: 锁定选择: EF 否/本机 SQL 是

    锁定更新

    当您修改 DB 中的行时,修改后的行会获得某种lock。锁的种类由正在运行的TransactionIsolation Level 决定。 SQL Server 中Isolation Level 的默认值为Read Committed,这意味着在当前事务中修改的所有行都获得Shared 锁定。此锁与SELECT 兼容,但与UPDATEDELETE 不兼容。这意味着当您修改事务中的一行时,默认情况下保证没有其他并行线程可以推断和更改它们,直到您通过COMMITROLLBACK 结束事务。

    .

    更新:

    表提示UPDLOCK and HOLDLOCK 可能会被查询优化器或其他 DBMS 模块忽略,因为它们只是 提示 :-)。唯一肯定强制执行的表提示组合是(XLOCK, PAGLOCK)

    Example: SELECT * FROM MyTable WITH (XLOCK, PAGLOCK)
    

    正如我所说,手动锁定是有风险的。以最大程度的考虑使用它。

    【讨论】:

    • 感谢您的回复,但正如您从我的代码中看到的那样,我已经知道(显然不是全部)。我只是不明白为什么我的代码不起作用。
    • 您能否提供一些示例代码来展示您在项目中是如何做到的?
    • 次要:SQL Server 永远不会忽略锁定命中。这是正确制作某些锁定模式所必需的。
    • @usr 表提示可能会被忽略。看看technet.microsoft.com/en-us/library/ms187373.aspx的Remark部分
    • 那么,有什么消息吗?如何在不使用 NativeSQL (TSQL) 的情况下在 EF 中实现悲观锁定?如何在 Select for further UPDATE 上实现 UPDLOCK?或者您对锁定 Select for Update 有任何其他想法吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    相关资源
    最近更新 更多