Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

huangapple go评论61阅读模式
英文:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s);

问题

This exception comes when I update my entity. And sometime entity is updated successfully.

Here is my table entity configurations:

Using EF Core and MySQL as database.

  1. Table 1: ParentEntity (Id is primary key and auto-incremented)
  2. Table 2: ChildEntity (CId is primary key and auto-incremented, Parent entity Id as foreign key).
  3. Table 3: SubChildEntity (SId is primary key and auto-incremented, CID as foreign key).

Error:

> Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was 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.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
> at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)

Code:

var Entity = await _context.Parent
                           .Include(x => x.Child)
                           .ThenInclude(x => x.SubChild)
                           .FirstOrDefaultAsync(x => x.Id == Id, cancellationToken);

// Some values to update in parent, child, and sub-child entities.
_context.Parent.Update(Entity);

var result = await _context.SaveChangesAsync(cancellationToken);
英文:

This exception comes when I update my entity. And sometime entity is updated successfully.

Here is my table entity configurations:

Using EF Core and MySQL as database.

  1. Table 1: ParentEntity (Id is primary key and auto-incremented)
  2. Table 2: ChildEntity (CId is primary key and auto-incremented, Parent entity Id as foreign key).
  3. Table 3: SubChildEntity (SId is primary key and autoincremented, CID as foreign key).

Error:

> Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was 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.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
> at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)

Code:

var Entity = await _context.Parent
                           .Include(x => x.Child)
                           .ThenInclude(x => x.SubChild)
                           .FirstOrDefaultAsync(x => x.Id == Id, cancellationToken);

// Some values to update in parent, child and sub child entities.
_context.Parent.Update(Entity);

var result = await _context.SaveChangesAsync(cancellationToken);

答案1

得分: 1

你可以通过将你的代码包装在try catch块中来进行调试。在catch块中,你可以查看数据库的值以及你提出的更新。这在[处理并发冲突][1]中有介绍。

例如:

try {
  // 你的原始代码
  var Entity = await _context.Parent
                           .Include(x => x.Child)
                           .ThenInclude(x => x.SubChild)
                           .FirstOrDefaultAsync(x => x.Id == Id, cancellationToken);

  // 需要更新的父级、子级和子子级实体的一些值。
  _context.Parent.Update(Entity);

  var result = await _context.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateConcurrencyException ex)
{
    // Microsoft提供的处理并发冲突的代码
    foreach (var entry in ex.Entries)
    {
        if (entry.Entity is Person)
        {
            var proposedValues = entry.CurrentValues; // 你提出的更改
            var databaseValues = entry.GetDatabaseValues(); // 数据库中的值

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

                // TODO: 决定要写入数据库的值
                // proposedValues[property] = <要保存的值>;
            }

            // 刷新原始值以绕过下一个并发检查
            entry.OriginalValues.SetValues(databaseValues);
        }
        else
        {
            throw new NotSupportedException(
                "不知道如何处理" + entry.Metadata.Name + "的并发冲突");
        }
    }
}

比较你的proposedValue和databaseValue以查看差异,以及可能导致冲突的原因。

从提供的信息来看,我猜测你可能在一个Web API或其他多线程应用程序中多次并发调用同一个函数(或者可能使用了类似Polly的重试库)。请计算函数被调用的次数以进行验证。

[1]: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=data-annotations#resolving-concurrency-conflicts

<details>
<summary>英文:</summary>

You can debug this by wrapping your code in a try catch block. In the catch you can view the database values as well as your proposed updates. This is covered in [Handling Concurrency Conflicts][1]. 

For example:

    try {
      //Your original code
      var Entity = await _context.Parent
                               .Include(x =&gt; x.Child)
                               .ThenInclude(x =&gt; x.SubChild)
                               .FirstOrDefaultAsync(x =&gt; x.Id == Id, cancellationToken);
    
      // Some values to update in parent, child and sub child entities.
      _context.Parent.Update(Entity);
    
      var result = await _context.SaveChangesAsync(cancellationToken);
    }
    catch (DbUpdateConcurrencyException ex)
    {
        //The code from Microsoft - Resolving concurrency conflicts 
        foreach (var entry in ex.Entries)
        {
            if (entry.Entity is Person)
            {
                var proposedValues = entry.CurrentValues; //Your proposed changes
                var databaseValues = entry.GetDatabaseValues(); //Values in the Db

                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] = &lt;value to be saved&gt;;
                }

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

Compare your proposedValue to the databaseValue to view the differences and what might be causing the conflict. 

My hunch, from the info provided, is that you are calling the same function multiple times concurrently if it is used in a web api or some other multithreaded app (or maybe with some kind of retry library like Polly). Count how many times the function is called to verify.

  [1]: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=data-annotations#resolving-concurrency-conflicts

</details>



huangapple
  • 本文由 发表于 2023年5月23日 00:27:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76308214.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定