【发布时间】:2013-08-22 15:05:59
【问题描述】:
使用 db first 方法,我希望我的应用程序在尝试更新(过期)实体时抛出并发异常,该实体在数据库中对应的行已被另一个应用程序/用户/会话更新。
我在 .Net 4.5 上使用实体框架 5。对应的表有一个 Timestamp 列来维护行版本。
【问题讨论】:
标签: concurrency timestamp entity-framework-5
使用 db first 方法,我希望我的应用程序在尝试更新(过期)实体时抛出并发异常,该实体在数据库中对应的行已被另一个应用程序/用户/会话更新。
我在 .Net 4.5 上使用实体框架 5。对应的表有一个 Timestamp 列来维护行版本。
【问题讨论】:
标签: concurrency timestamp entity-framework-5
我过去通过在您希望执行并发检查的表中添加时间戳字段来完成此操作。 (在我的示例中,我添加了一个名为 ConcurrencyCheck 的列)
这里有两种并发模式,根据你的需要:
1 并发模式:固定:
然后在您的模型中重新添加/刷新您的表格。对于固定并发,确保在将表导入模型时将并发模式设置为固定:像这样:
然后捕获这个:
try
{
context.SaveChanges();
}
catch (OptimisticConcurrencyException ex) {
////handle your exception here...
2。并发模式:无
如果您希望处理自己的并发检查,即提出验证通知用户,甚至不允许保存发生,那么您可以设置并发模式无。
1.确保将刚刚添加的新列的属性中的ConcurrencyMode更改为“无”。 2. 要在您的代码中使用它,我将创建一个变量来将您当前的时间戳存储在您要检查保存的屏幕上。
private byte[] CurrentRecordTimestamp
{
get
{
return (byte[])Session["currentRecordTimestamp"];
}
set
{
Session["currentRecordTimestamp"] = value;
}
}
1.在页面加载时(假设您使用的是 asp.net 而不是上面未提及的 mvc/razor),或者当您使用希望编辑的数据填充屏幕时,我会在下面拉出当前记录将 ConcurrencyCheck 值编辑到您创建的此变量中。
this.CurrentRecordTimestamp = currentAccount.ConcurrencyCheck;
然后,如果用户让记录保持打开状态,同时其他人更改了它,然后他们也尝试保存,您可以将之前保存的这个时间戳值与现在的并发值进行比较。
if (Convert.ToBase64String(accountDetails.ConcurrencyCheck) != Convert.ToBase64String(this.CurrentRecordTimestamp))
{
}
【讨论】:
MetadataType 以便在修改 EDMX 时属性不会丢失...吗?
在查看了这里和网络上许多解释 Entity Framework 5 中的并发和时间戳的帖子后,我得出的结论是,当模型从现有数据库生成时,基本上不可能出现并发异常。
一种解决方法是修改 .edmx 文件中生成的实体,并将实体的时间戳属性的“并发模式”设置为“固定”。不幸的是,如果模型反复从数据库中重新生成,这种修改可能会丢失。
但是,有一个棘手的解决方法:
使用可重复读取或更高的隔离级别初始化事务范围
获取行的时间戳
比较新时间戳和旧时间戳
不等于 --> 异常
相等 --> 提交事务
隔离级别对于防止并发修改推断很重要。
PS: Erikset 的解决方案似乎很好地克服了重新生成模型文件。
【讨论】:
如果没有行受到影响,EF 会检测到并发冲突。然后,如果您使用存储过程来删除和更新,您可以在 where 子句中手动添加时间戳值:
UPDATE | DELETE ... WHERE PKfield = PkValue and Rowversionfield = rowVersionValue
那么如果该行已被其他人删除或修改,Sql 语句会影响 0 行,EF 将其解释为并发冲突。
【讨论】: