实体框架的原子增量

时间:2013-07-31 13:04:24

标签: c# asp.net mysql entity-framework entity-framework-4.1

我有一个使用Entity Framework 4.0访问的MySQL服务器。在数据库中,我有一个名为 Works 的表,其中有些计数。我用Asp.net开发网站。该表可以在同一时间再次使用。而这种情况会导致错误的推理问题。

我的代码就是这样:

dbEntities myEntity = new dbEntities();

var currentWork = myEntity.works.Where(xXx => xXx.RID == 208).FirstOrDefault();
Console.WriteLine("Access work");

if (currentWork != null)
{
    Console.WriteLine("Access is not null");
    currentWork.WordCount += 5;//Default WordCount is 0
    Console.WriteLine("Count changed");
    myEntity.SaveChanges();
    Console.WriteLine("Save changes");
}
Console.WriteLine("Current Count:" + currentWork.WordCount);

如果一个以上的线程同时访问数据库,则仅保留最后一个更改。

当前输出:

t1:线程一 - t2:线程二

  

t1:访问工作

     

t2:访问工作

     

t2:访问不为空

     

t1:访问不为空

     

t1:计数已更改

     

t2:计数已更改

     

t1:保存更改

     

t2:保存更改

     

t1:当前计数:5

     

t2:当前计数:5

预期产出:

  

t1:访问工作

     

t2:访问工作

     

t2:访问不为空

     

t1:访问不为空

     

t1:计数已更改

     

t2:计数已更改

     

t1:保存更改

     

t2:保存更改

     

t1:当前计数:5

     

t2:当前计数:10

我知道为什么会遇到这个问题,因为这段代码不是原子的。我怎样才能改变原子操作?

2 个答案:

答案 0 :(得分:14)

使用Entity Framework,您无法将其作为“原子”操作。你有以下步骤:

  1. 从数据库加载实体
  2. 更改内存中的计数器
  3. 将更改的实体保存到数据库
  4. 在这些步骤之间,另一个客户端可以从仍具有旧值的数据库加载实体。

    处理这种情况的最佳方法是使用乐观并发。它基本上意味着如果计数器不再与您在步骤1中加载实体时的计数器不同,则不会保存步骤3中的更改。相反,您将获得一个可以通过重新加载实体来处理的异常,重新申请改变。

    工作流程如下所示:

    • Work实体中,WordCount属性必须标记为并发令牌(如果是代码优先,则为注释或Fluent API)
    • 从数据库加载实体
    • 更改内存中的计数器
    • SaveChanges块中调用try-catch并捕获DbUpdateConcurrencyException类型的例外
    • 如果发生异常,请从数据库重新加载catch块中的实体,再次应用更改并再次致电SaveChanges
    • 重复最后一步,直到不再发生异常

    this answer中,您可以找到此过程的代码示例(使用DbContext)。

答案 1 :(得分:0)

如果您在单个流程中托管您的网站(它不适用于网络服务器场或网络gardsen),则下一步将起作用:

   private static readonly Locker = new object();

   void Foo()
   {
          lock(Locker)
          {
                 dbEntities myEntity = new dbEntities();

                 var currentWork = myEntity.works.Where(xXx => xXx.RID == 208).FirstOrDefault();
                 Console.WriteLine("Access work");

                 if (currentWork != null)
                 {
                     Console.WriteLine("Access is not null");
                     currentWork.WordCount += 5;//Default WordCount is 0
                     Console.WriteLine("Count changed");
                     myEntity.SaveChanges();
                     Console.WriteLine("Save changes");
                 }
                 Console.WriteLine("Current Count:" + currentWork.WordCount);
          }
   }

你还能做什么,是通过ObjectContext使用原始SQL查询:

   if (currentWork != null)
             {
                 Console.WriteLine("Access is not null");
                 myEntity.ExecuteStoredCommand("UPDATE works SET WordCount = WordCount + 5 WHERE RID = @rid", new MySqlParameter("@rid", MySqlDbType.Int32){Value = 208)
                 Console.WriteLine("Count changed");

             }
    var record = myEntity.works.FirstOrDefault(xXx => xXx.RID == 208);
    if(record != null)
        Console.WriteLine("Current Count:" + record .WordCount);