我正在使用 ASP.NET 为一家零售公司构建应用程序。我使用 Entity Framework (模型优先)作为我的数据访问层。我正在使用存储过程来执行我的 CRUD 操作,并且所有列都已映射并且似乎是正确的,因为所有 CRUD 功能都按预期工作。
但是我遇到了 DELETE 操作的并发问题。
我已将 TimeStamp
列添加到我正在执行 CRUD 操作的表中。 UPDATE 操作工作正常,因为它通过主键和 TimeStamp
值进行更新。因此,如果由于 TimeStamp
值的更改,没有行受到 UPDATE 操作的影响, Entity Framework 将抛出 OptimisticConcurrencyException
。
DELETE
操作的工作原理与通过主键和 TimeStamp
值删除相同。但是当 TimeStamp
值在实体和数据库之间不匹配时不会抛出异常。
在 C# delete 方法中,我首先检索最新记录,然后将 TimeStamp
属性更新为另一个 TimeStamp
值(它可能与检索到的值不同)。在使用 SQL Profiler 进行一些调查后,我可以看到执行了 DELETE
存储过程,但是传递给存储过程的 TimeStamp
参数是最新的 TimeStamp
值 而不是我将 TimeStamp
属性设置为 的值。因此记录被删除, Entity Framework 不会抛出异常。
为什么 Entity Framework 仍会将检索到的 TimeStamp
值传递给存储过程,而不是我为属性分配的值?这是设计还是我遗漏了什么?
任何帮助将不胜感激! (当你需要 Julie Lerman 的时候她在哪里!:-))
最佳答案
EF 中的开放式并发运行良好。即使使用存储过程。
ObjectContext.DeleteObjects
将实体的原始值传递给删除函数。这是有道理的。原始值用于标识要删除的行。当您删除对象时,您(通常)不会对您的实体进行有意义的编辑。那么您希望 EF 做什么?写?到什么记录?
将修改后的数据传递给删除函数的一个合法用途是当您想要跟踪其他表中的删除时并且您需要输入一些在数据库层无法访问的信息,只能在业务层访问。示例包括应用程序级用户名或删除原因。在这种情况下,您需要使用此值作为原始值 来构建实体。一种方法:
var x = db.MyTable.Single(k => k.Id == id_to_delete);
x.UserName = logged_in_user;
x.ReasonForChange = some_reason;
// [...]
db.ObjectStateManager.ChangeObjectState(x, EntityState.Unchanged);
db.MyTable.DeleteObject(x);
db.SaveChanges();
当然,更好的策略可能是在业务层公开进行。
我不明白您使用 rowversion/timestamp 的用例。
为避免并发问题,您将原始 时间戳传递给修改代码。 这样就可以将它与数据库中的当前值进行比较,以检测记录自上次读取以来是否发生了变化。 将它与新值进行比较毫无意义。
您通常使用由数据库自动更新的更改标记,例如 SQL Server 中的 rowversion/timestamp、Oracle 中的 rowversion 或 PostgreSQL 中的 xmin。 您不会在代码中更改它的值。
不过,如果您手动维护行版本,则需要提供:
a) 要插入和更新的新版本,以及
b) 更新和删除旧版本(从数据库读取)以检查并发更改。
您不发送要删除的新值。你不需要。 此外,在使用存储过程进行修改时,最好在过程中计算新版本并将其返回给应用程序,而不是相反。
关于c# - 使用存储过程的 Entity Framework 并发问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12093135/