实体框架核心异步更新失败,受影响的行为0

时间:2018-09-24 11:42:57

标签: c# entity-framework-core

我在Xamarin移动项目中具有以下插入和更新代码:

public async Task SaveData(T data, bool update)
{
  try
  {
    if (update)
      dbContext.Set<T>().Attach(data);
    else
      await dbContext.Set<T>().AddAsync(data);
    await dbContext.SaveChangesAsync();
  }
  catch(Exception ex)
  {
    Log();
    throw;
  }
}

当我更新现有记录时,我总是会遇到异常:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
  at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException (System.Int32 commandIndex, System.Int32 expectedRowsAffected, System.Int32 rowsAffected) [0x0001e] in <e12f0cc891a249419803faaf433b12e6>:0 
  at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch+<ConsumeResultSetWithoutPropagationAsync>d__6.MoveNext () [0x000e9] in <e12f0cc891a249419803faaf433b12e6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch+<ConsumeAsync>d__2.MoveNext () [0x0016f] in <e12f0cc891a249419803faaf433b12e6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch+<ExecuteAsync>d__32.MoveNext () [0x00142] in <e12f0cc891a249419803faaf433b12e6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor+<ExecuteAsync>d__10.MoveNext () [0x00220] in <e12f0cc891a249419803faaf433b12e6>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager+<SaveChangesAsync>d__79.MoveNext () [0x00088] in <159bf28779664ac6914c3caf595e15c9>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager+<SaveChangesAsync>d__77.MoveNext () [0x000f8] in <159bf28779664ac6914c3caf595e15c9>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at Microsoft.EntityFrameworkCore.DbContext+<SaveChangesAsync>d__52.MoveNext () [0x000cf] in <159bf28779664ac6914c3caf595e15c9>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x0003e] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in <43dbbdc147f2482093d8409abb04c233>:0 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <43dbbdc147f2482093d8409abb04c233>:0 
  at MyXamarin.DatabaseProject.Repository.SaveData [0x0036f]

这是我在Entity Framework Core日志中看到的:

[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.ChangeTracking[10801]
      DetectChanges completed for 'MyContext'.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Connection[20000]
      Opening connection to database 'main' on server '/data/data/com.example.MyXamarinProject/files/XamarinDB.db'.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Connection[20001]
      Opened connection to database 'main' on server '/data/data/com.example.MyXamarinProject/files/XamarinDB.db'.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Command[20100]
      Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
      PRAGMA foreign_keys=ON;
[40m[32minfo[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      PRAGMA foreign_keys=ON;
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Transaction[20200]
      Beginning transaction with isolation level 'Serializable'.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Command[20100]
      Executing DbCommand [Parameters=[@p1='3201a98f-36b2-4b8c-87d2-4f7f85221b28' (DbType = String), @p0='55' (Size = 2)], CommandType='Text', CommandTimeout='30']
      UPDATE "Detail" SET "Level" = @p0
      WHERE "DetailID" = @p1;
      SELECT changes();
[40m[32minfo[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (4ms) [Parameters=[@p1='3201a98f-36b2-4b8c-87d2-4f7f85221b28' (DbType = String), @p0='55' (Size = 2)], CommandType='Text', CommandTimeout='30']
      UPDATE "Detail" SET "Level" = @p0
      WHERE "DetailID" = @p1;
      SELECT changes();
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Command[20300]
      A data reader was disposed.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Transaction[20204]
      Disposing transaction.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Connection[20002]
      Closing connection to database 'main' on server '/data/data/com.example.MyXamarinProject/files/XamarinDB.db'.
[40m[37mdbug[39m[22m[49m: Microsoft.EntityFrameworkCore.Database.Connection[20003]
      Closed connection to database 'main' on server '/data/data/com.example.MyXamarinProject/files/XamarinDB.db'.
[41m[30mfail[39m[22m[49m: Microsoft.EntityFrameworkCore.Update[10000]
      An exception occurred in the database while saving changes for context type 'MyXamarinProject.Database.MyContext'.
      Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

根据错误消息中的link,这可能是一些并发问题。

我根据该文章修改了代码,以检查数据库中是否存在该实体:

public async Task SaveData(T data, bool update)
{
  try
  {
    if (update)
      dbContext.Set<T>().Attach(data);
    else
      await dbContext.Set<T>().AddAsync(data);
    var dbValue = GetFromDb<Detail>(b => b.DetailID == Guid.Parse("3201a98f-36b2-4b8c-87d2-4f7f85221b28")); //dbValue does exist!!!
    await dbContext.SaveChangesAsync();
  }
  catch (DbUpdateConcurrencyException dbex)
  {
    foreach (var entry in dbex.Entries)
    {
      Console.WriteLine(entry.State); //it is Microsoft.EntityFrameworkCore.EntityState.Modified
      if (entry.Entity is Detail)
      {
        var proposedValues = entry.CurrentValues;
        var databaseValues = entry.GetDatabaseValues(); //IT IS ALWAYS NULL!!!

        foreach (var property in proposedValues.Properties)
        {
          var proposedValue = proposedValues[property];
          var databaseValue = databaseValues[property];

          // TODO: decide which value should be written to database
          // proposedValues[property] = <value to be saved>;
        }

        // Refresh original values to bypass next concurrency check
        entry.OriginalValues.SetValues(databaseValues);
      }
      else
      {
        throw new NotSupportedException(
            "Don't know how to handle concurrency conflicts for "
            + entry.Metadata.Name);
      }
    }
  }    

正如GetDatabaseValues()所建议的那样,似乎在某种程度上“丢失”了真实的数据库。

我该怎么办?

奖金 :当我尝试添加新实体时,总是会遇到外键问题,就像导航属性所引用的实体是不存在。

更新

当我使用NoTracking从数据库中获取实体时,在保存期间,它将尝试在另一个实体中插入一个 navigation属性的新实体,该实体是Detail。如果导航属性为null,为什么Entity Framework感觉需要插入一个全新的实体?

更新2

它看起来像Entity Framework Core中的一些严重错误。

如果我运行以下行,则会得到每个实体:

var full = await dbContext.Detail.ToListAsync();

如果运行以下行,则会得到要更新的实体:

var fullFilter = full.Where(b => b.DetailID == data.DetalID).FirstOrDefault();

如果我运行以下行,则会得到0个实体:

var actual = dbContext.Detail.Where(b => b.DetailID == data.DetalID).FirstOrDefault();

我想后者是Entity Framework Core在内部使用的...

有什么区别?

0 个答案:

没有答案