16

我有一个使用 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

我知道为什么会出现这个问题,因为这段代码不是原子的。我怎样才能打开原子操作?

4

2 回答 2

16

使用实体框架,您不能将其设为“原子”操作。你有以下步骤:

  1. 从数据库加载实体
  2. 更改内存中的计数器
  3. 将更改的实体保存到数据库

在这些步骤之间,另一个客户端可以从数据库中加载仍然具有旧值的实体。

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

工作流程如下所示:

  • Work实体中,WordCount必须将属性标记为并发令牌(在 Code-First 的情况下,注释或 Fluent API)
  • 从数据库加载实体
  • 更改内存中的计数器
  • 调用块并捕获类型SaveChanges的异常try-catchDbUpdateConcurrencyException
  • 如果发生异常catch从数据库重新加载块中的实体,再次应用更改并再次SaveChanges调用
  • 重复最后一步,直到不再出现异常

这个答案中,您可以找到此过程的代码示例(使用DbContext)。

于 2013-07-31T17:16:58.817 回答
0

如果您在单个进程中托管您的网站,下一个方法将起作用(它不适用于网络农场或网络花园):

   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);
于 2013-07-31T13:13:40.737 回答