我有一个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都需要显式交易吗?我认为该框架应该为我做到这一点。
答案 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 tran
将automatically rollback,并且WebAPI2将向客户端发送一个不错的500响应。
P.S。我也将db = new MyContext
放在使用中,因为这是正确的方法。