2017-06-17 52 views
3

我在带有Web客户端的Web API服务器上使用EF 6.x(代码优先),我需要实现并发处理。问题是我甚至无法让EF生成异常。EF不会抛出DbUpdateConcurrencyException,尽管发生冲突更新

我发现的大多数例子似乎都没有使用“分离的实体”,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将公司名称更改为“Acme,The Great!”并点击保存(从浏览器)

4)公司DTO到达Web API服务器CompanyName =“Acme,The Great!”和老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

您描述的问题没有并发问题。您正在从数据库获取最新版本的数据并对其进行更新并保存。为什么EF会遇到问题。如果正在更新的行已被删除或被其他查询锁定,或者两个进程试图同时更改同一行,则这些coluld会出现问题。但大部分时间,EF和SQL Server都尽力处理它。 –

+1

@ChetanRanpariya我的想法是,EF会检测到我试图保存的记录的RowVersion与DB中的记录不匹配,从而知道该记录很脏。如果不是这种情况,在这种情况下处理上述并发问题的正确/最佳做法是什么? – Lars335

+0

在您的示例中,您不保存之前检索的行。您正在检索并保存最新版本的行。我不确定你在这里有什么打算。您是在解决真正的应用程序问题还是只是开始了解EF?这里的用例究竟是什么?如果实体被其他进程修改,您是否希望EF引发异常?你可以查看https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework -in-an-asp-net-mvc-application –

回答

3

我得到了这个工作。在我的问题的第5步,我改变了这一行:

dbCompany.RowVersion = updatedCompany.RowVersion; 

要这样:

Context.Entry(dbCompany).OriginalValues["RowVersion"] = updatedCompany.RowVersion; 

现在EF试图挽救脏数据时抛出一个DbUpdateConcurrencyException!

+0

太棒了。我看到的大多数教程都使用TryUpdateModel,我拒绝在我的MVC应用程序中使用它。这为我做了诡计。 – William