使用Entity Framework的“下一个号码”场景

时间:2011-02-05 16:43:04

标签: entity-framework-4 linq-to-entities pessimistic-locking

我一直在玩Entity Framework,到目前为止我喜欢使用它。但到目前为止我所做的一切都假设乐观锁定,在大多数情况下对我来说效果很好。但是,我有以下情况:

  1. 只包含一行保存应用程序范围数据的SQL Server表
  2. 该行包含名为“NextAvailableNumber”
  3. 的列
  4. 应用程序需要读取数字,将其递增1并更新它
  5. 以上必须保证在第一笔交易完成之前,任何竞争过程都必须等待获得一个数字。以前我们使用表锁来完成此操作(因为只有一行),但我想知道如何使用LINQ to Entities完成此操作?

    谢谢,

    Jim K。

3 个答案:

答案 0 :(得分:1)

我认为你需要在存储过程中实现它,因为并使用显式行锁定(或如上所述的表锁定)。然后你将从EF调用该程序。我不认为这可以从应用程序代码处理,除非您每次使用特殊表时都使用可序列化事务。它会对应用程序的性能产生巨大的负面影响。

我们实际上正在做类似的事情,但是我们的表包含大量不同序列的行,所以我使用带有行锁和更新锁的存储过程。我们首先想在我们的存储库的insert方法中调用此过程,但之后我将其移动到数据库中,并在插入触发器之后调用我的过程。这样做的原因是将行锁定应用到实际插入数据库的时间,而不是在EF上下文中标记实体插入的时间(在我们的例子中,插入本身和过程调用必须在同一个事务中)。我修改了我的EF模型,以便相关属性将StoreGeneratedPattern设置为computed。每次插入后,EF将重新查询DB以获取指定的序列号。唯一的缺点是,EF会在每次更新这些实体后重新查询数据库,但在我们的情况下,由于时间戳,它已经这样做了。这仍处于测试和评估的阶段,所以我仍然可以改变主意并重新实现它。

答案 1 :(得分:1)

我们正在使用另一种解决方案,您可以使用以避免使用存储过程,特别是如果您像我们一样使用EF Code First。 Code First不支持使用商店程序。

解决方案是在事务范围内使用EF的SqlQuery方法,你必须编写一个查询,首先更新下一个数字计数器,然后选择它。代码将是:

string query = "UPDATE Registre_Counter SET Counter = Counter + 1 WHERE CounterName = @p0 AND Year = @p1;";
query += "select * from Registre_Counter Where CounterName = @p0 AND Year = @p1";

GenericCounter GenericCounter = CoreDBContext.Instance().GenericCounter.SqlQuery(query, new Object[] { _counterName, _year }).SingleOrDefault(); //Updates the counter and return de NextNumber to Use

更新将锁定计数器并避免从其上的其他事务中读取,直到transaccion被中止或提交。

答案 2 :(得分:0)

由于Entity框架使用乐观锁定,因此当第二个进程更改下一个数字时,它将引发异常。解决这种情况的最原始的方法是捕获异常并重做更改,直到更改成功。 [您可以为此添加超时以确保代码执行在最坏的情况下向前移动]