我正在尝试Catch DbUpdateConcurrencyException
在调试时,我得到了entity.RowVersion与productEdit.RowVersion不同。
但是我不知道为什么该程序可以成功保存数据。
public async Task<IActionResult> Patch(Guid id, [FromBody] ProductEditModel productEdit)
{
var entity = await this.unitOfWork.Products.GetByIdAsync(id);
this.mapper.Map(productEdit, entity);
try
{
await this.unitOfWork.SaveAsync(); // <<--- here.
}
catch (DbUpdateConcurrencyException ex)
{
return Conflict(ex);
}
return Ok();
}
这是我的实体类:
public abstract class BaseEntity
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class Product : BaseEntity
{
public String Name { get; set; }
public String Description { get; set; }
public double Price { get; set; }
}
答案 0 :(得分:1)
here描述了类似的问题。它是关于EF核心的,但是我也已经对其进行EF6测试,并且在那里也一样。
最重要的是,EF将RowVersion
属性的原始值与当前数据库值进行比较。此比较是SQL update命令的一部分,该命令包含一个WHERE
子句:
UPDATE ...
WHERE [ID] = @p1 AND [RowVersion] = @p2;
@p2
参数是RowVersion
属性的原始值。如果版本不匹配,则会抛出DbUpdateConcurrencyException
。
所以这里有问题...
this.mapper.Map(productEdit,entity);
...仅更新RowVersion
的当前值。
因此如果您要操纵RowVersion
的值,则必须更改原始值。为此,您需要实体条目,如下所示:
context.Entry(entity).Property(e => e.RowVersion).OriginalValue = rowversion;
由于您将上下文包装在工作单元/存储库模式中,因此必须了解如何实现此目的。
但是,您可能需要考虑其他方法。我认为您不应该从数据库中获取Product
实体并设置其值并操纵其RowVersion
。将productEdit
映射到新的Product
实体对象,然后将该对象附加到上下文中更为合适。这会将RowVersion
的原始值自动设置为您想要的版本。作为奖励,它节省了往返数据库的时间。