为什么没有带有实体框架的WebAPI2会自动为我创建一个交易?

时间:2016-04-22 21:10:35

标签: entity-framework transactions asp.net-web-api2

我有一个WebAPI2 Restful服务API,我正在使用带有Entity Framework的SQL Server数据库。我有PUT这样的方法

    /* 
     * This changes the Study Status.
     */
    [HttpPut, Route("ResponseSetStatus/{id:int}")]
    public IHttpActionResult UpdateResponseSetStatus(int id, [FromUri] string status = null)
    {
        var db = new MyContext(MyContext.EntityContextString);
        var responseSet = db.ResponseSets.FirstOrDefault(x => x.ResponseSetId == id);

        if (responseSet == null)
        {
            return NotFound();
        }

        // ADD ONE SECOND DELAY HERE FOR TESTING
        Thread.Sleep(1000);
        responseSet.Status = status;
        db.SaveChanges();
        return Ok();
    }

想到这会起作用!但它失败了。数据库中的一列是rowVersion(以防止丢失更新)。当我从多个客户端调用此函数时,我得到异常......

  

类型' System.Data.Entity.Infrastructure.DbUpdateConcurrencyException'的例外情况发生在EntityFramework.dll中但未在用户代码中处理

因为rowVersion不匹配。我真的我的所有更新api都需要显式交易吗?我认为该框架应该为我做到这一点。

1 个答案:

答案 0 :(得分:0)

由于没有人回答,我会的。是的,WebAPI2在事务中包装调用。如果你考虑一下,这将是愚蠢的。代码

using (var db = new MyContext()) {
    // do stuff
}

隐式创建交易。因此,当您实现RESTFUL PUT方法来更新数据库时,您有三个选择:(1)调用db.SaveChanges() one time only and hope for the best,作为OP代码,或者(2)您可以添加rowVersion列,并使用try-catch in a loop致电db.SaveChanges(),或(3)您可create an explicit transaction

在我看来,选项1是邪恶的,而选项2是一个可怕的黑客,因为在EF6之前不存在交易。

实现更新的正确方式:

[HttpPut, Route("ResponseSetStatus/{id:int}")]
public IHttpActionResult UpdateResponseSetStatus(int id, [FromUri] string status = null)
{
    using (var db = new MyContext(MyContext.EntityContextString))
    {
        using (var tran = db.Database.BeginTransaction()) 
        { 
            var responseSet = db.ResponseSets.FirstOrDefault(x => x.ResponseSetId == id);

            if (responseSet == null)
            {
                return NotFound();
            }

            // ADD ONE SECOND DELAY HERE FOR TESTING
            Thread.Sleep(1000);
            responseSet.Status = status;
            tran.Commit();
        }
    }
    return Ok();
}

请注意,不需要try-catch。如果有任何失败,using tranautomatically rollback,并且WebAPI2将向客户端发送一个不错的500响应。

P.S。我也将db = new MyContext放在使用中,因为这是正确的方法。