我在带有Web客户端的Web API服务器上使用EF 6.x(代码优先),我需要实现并发处理。问题是我甚至无法获得EF来生成异常。
我发现的大多数示例似乎都没有使用“分离实体”,其中DTO被发送到Web客户端,在那里它被更新,然后在以后保存回服务器(这是我的方案)。
假设我有公司记录:
public class Company
{
int CompanyId { get; set; }
string CompanyName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
1)用户A提取公司Id
= 0,RowVersion
为0x0000000000002B0A
2)我运行UPDATE Company SET CompanyName = 'Acme Changed, Inc.' WHERE CompanyId = 0
来模拟其他用户的更改。 RowVersion
更改为0x0000000000002B0B
3)用户A将CompanyName更改为“Acme,The Great!”并单击“保存”(从浏览器)
4)公司DTO通过CompanyName
=“Acme,The Great!”到达Web API服务器。和旧RowVersion
= 0x0000000000002B0A
5)我从数据库中检索公司记录,更新并保存:
public void UpdateCompany(Company updatedCompany)
{
var dbCompany = Context.Companies.SingleOrDefault(r => r.CompanyId == updatedCompany.CompanyId);
dbCompany.CompanyName = updatedCompany.CompanyName;
dbCompany.RowVersion = updatedCompany.RowVersion; // Set RowVersion to the passed in original RowVersion 0x0000000000002B0A
try
{
DbContext.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
// Expected: exception thrown (but does not happen).
}
}
它只保存记录并将RowVersion
更新为0x0000000000002B0C而不是并发异常。
我错过了什么?
我需要一种方法来检测更改,删除等,以防止保存脏数据。我想我可以滚动自己的检查,但实际的对象很复杂,有很多嵌套的子对象(一个或多个级别)。
关于此的最佳做法的任何指示也将受到赞赏......
答案 0 :(得分:8)
我得到了这个工作。在我的问题的第5步中,我更改了这一行:
dbCompany.RowVersion = updatedCompany.RowVersion;
对此:
Context.Entry(dbCompany).OriginalValues["RowVersion"] = updatedCompany.RowVersion;
现在,EF在尝试保存脏数据时会抛出DbUpdateConcurrencyException!
答案 1 :(得分:0)
我正在使用Entity Framework的抽象,但无法访问EF上下文,但是我发现写作
updatedCompany.RowVersion.CopyTo(dbCompany.RowVersion, 0);
解决了问题。
答案 2 :(得分:0)
在@ Lars335之后,我写了一个小助手方法来重置并发令牌的原始值
public static void ResetConcurrencyValues(this DbContext context, Object entity) {
var lEntry = context.Entry(entity);
foreach (var lProperty in lEntry.Metadata.GetProperties().Where(x => x.IsConcurrencyToken)) {
lEntry.OriginalValues[lProperty] = lEntry.CurrentValues[lProperty];
}
}
然后使记录更新成为
_context.Update(user);
_context.ResetConcurrencyValues(user);
var lCount = await _context.SaveChangesAsync(cancellationToken);
但是,我也很想,如果令牌的 current 值与存储的值不同,则应该引发并发异常。在进行了冲突的更改之前,无法重新读取更新的记录,因此永远不会包含原始值。