_context.SaveChanges() 有效,但等待 _context.SaveChangesAsync() 无效

Fab*_*uez 4 asp.net-core ef-core-2.2 pomelo-entityframeworkcore-mysql

我正在努力理解一些事情。我有一个 .Net Core 2.2 Web API,带有 MySQL 8 数据库,并使用 Pomelo 库连接到 MySQL 服务器。

我有一个 PUT 操作方法,如下所示:

// PUT: api/Persons/5
[HttpPut("{id}")]
public async Task<IActionResult> PutPerson([FromRoute] int id, Person person)
{
    if (id != person.Id)
    {
        return BadRequest();
    }

    _context.Entry(person).State = EntityState.Modified;

    try
    {
        _context.SaveChanges(); // Works
        // await _context.SaveChangesAsync(); // Doesn't work
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!PersonExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}
Run Code Online (Sandbox Code Playgroud)

根据我在上面的代码片段中的评论,当我调用_context.SaveChanges()时,它起作用(即它更新 MySQL 数据库中的相关记录,并返回 1),但是当我调用wait _context.SaveChangesAsync()时,它不起作用(它不更新记录,并且返回 0)。它不会抛出异常或任何异常 - 它只是不更新​​记录。

有任何想法吗?

Chr*_*att 5

正如我在上面的评论中所说,EF Core 没有真正的同步方法。同步方法(例如SaveChanges)仅阻塞异步方法(例如SaveChangesAsync)。因此,如果SaveChanges不行的话,这是不可能的SaveChangesAsync,因为前者只是后者的代理。这里还有一些其他问题,从您提供的代码中看不出来。

然而,我写这篇文章作为答案的原因是,一般来说,你这样做的方式是错误的,我相信只要做得正确,问题可能就会消失。你永远不应该,我的意思是永远不要直接将从请求正文创建的实例直接保存到数据库中。这提供了一个攻击媒介,允许恶意用户以不良方式更改您的数据库。您已经通过检查 id 是否未被修改来部分覆盖了这一点,但用户仍然可以更改他们不应该被允许的内容。

除了安全漏洞之外,还有一个实际的理由不这样做。API 可以充当反腐败层,但前提是您将实体与客户端交互的对象分离。当您直接使用实体时,您将数据库与 API 层紧密耦合,这样数据库级别的任何更改都需要新版本的 API,更糟糕的是,没有机会弃用以前的版本。所有客户端都必须立即更新,否则其实施将会中断。通过向客户端公开 DTO 类,数据库可以独立于 API 进行发展,因为您可以添加任何必要的反腐败逻辑来弥合两者之间的差距。

长话短说,你的方法应该是这样构建的:

// PUT: api/Persons/5
[HttpPut("{id}")]
public async Task<IActionResult> PutPerson([FromRoute] int id, PersonModel model)
{
     // not necessary if using `[ApiController]`
    if (!ModelState.IsValid)
        return BadRequest();

    var person = await _context.People.FindAsync(id);
    if (person == null)
        return NotFound();

    // map `model` onto `person`

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        // use an optimistic concurrency strategy from:
        // https://learn.microsoft.com/en-us/ef/core/saving/concurrency#resolving-concurrency-conflicts
    }

    return NoContent();
}
Run Code Online (Sandbox Code Playgroud)

我想让代码保持简单,但为了处理乐观并发,我实际上建议使用 Polly 异常处理库。您可以设置重试策略,以便在错误纠正后继续尝试进行更新。否则,您需要在 try/catch 中的 try/catch 中的 try/catch 等。此外,DbUpdateConcurrencyException您应该始终以某种方式处理它,因此重新抛出它是没有意义的。


Fab*_*uez 5

对于那些因为我的问题而浪费时间的人,我深表歉意。我找出了问题所在,这是我在 dbContext 中犯的一个愚蠢的错误。我有一个审计跟踪设置,所以我优先考虑SaveChangesAsync,OnBeforeSaveChangesOnAfterSaveChanges。该代码中有一个错误。然而,我并没有压倒一切SaveChanges,这就是为什么它仍然有效。对不起!

  • 你不需要道歉,你也帮助了我,现在我知道当 SaveChanges 和 SaveChangesAsync 有不同的结果时应该做什么检查 (2认同)