asp.net-core - _context.SaveChanges() 有效但等待 _context.SaveChangesAsync() 无效

标签 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();
}

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

有任何想法吗?

最佳答案

正如我在上面的评论中所说,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://docs.microsoft.com/en-us/ef/core/saving/concurrency#resolving-concurrency-conflicts
    }

    return NoContent();
}

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

关于asp.net-core - _context.SaveChanges() 有效但等待 _context.SaveChangesAsync() 无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57854317/

相关文章:

c# - 如何在 ASP.NET Core 中自定义配置绑定(bind)

c# - 什么会导致 DbSet 中的持久化实体分离?

entity-framework - EF Core 将鉴别器列添加到拥有的实体

mysql - .NET Core 添加迁移忽略一些属性

c# - 使用自定义货币代码格式化货币(ASP.NET Core MVC)

asp.net-core - 将数组传递给 asp net core web api 操作方法 HttpGet

postgresql - 将 PostgreSQL 安装到 asp.net core 容器中

c# - 测试最小起订量 : System. NotSupportedException : Unsupported expression: c => c. 价格

mysql - 在 ASP.NET Core 2.0 预览版中使用 Pomelo MySQL EF 提供程序时出错

mysql - Pomelo.EntityFrameworkCore.MySql MySqlGeometry,EntityframeworkCore 迁移失败