_context.SaveChanges()有效,但是等待_context.SaveChangesAsync()不起作用

时间:2019-09-09 12:43:35

标签: asp.net-core ef-core-2.2 pomelo-entityframeworkcore-mysql

我正在努力了解一些东西。我有一个.Net Core 2.2 Web API,带有一个MySQL 8数据库,并使用Pomelo库连接到MySQL Server。

我有一个如下所示的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)。它不会引发异常或其他任何事件-它只是不会更新记录。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

正如我在上面的评论中所述,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是您应始终以某种方式处理的东西,因此重新抛出它毫无意义。

答案 1 :(得分:1)

对于那些浪费我这个时间的人,我深表歉意。我发现了问题,这是我在dbContext中犯的一个愚蠢的错误。我有一个审计跟踪设置,所以我将覆盖SaveChangesAsync,OnBeforeSaveChanges和OnAfterSaveChanges。该代码中有一个错误。但是,我没有重写SaveChanges,这就是为什么它仍然起作用。抱歉!