更新(2010-12-21):根据我一直在做的测试,完全重写了这个问题。另外,这曾经是POCO特定的问题,但事实证明,我的问题不一定是POCO特定的。
我正在使用Entity Framework,并且我的数据库表中有一个timestamp列,该列应用于跟踪乐观并发的更改。我已在实体设计器中将此属性的并发模式设置为“固定”,并且得到的结果不一致。以下是几个简化的场景,它们演示了并发检查在一种情况下有效,而在另一种情况下无效。
成功引发OptimisticConcurrencyException:
如果我附加了断开连接的实体,那么如果存在时间戳冲突,则SaveChanges将引发OptimisticConcurrencyException:
[HttpPost]
public ActionResult Index(Person person) {
_context.People.Attach(person);
var state = _context.ObjectStateManager.GetObjectStateEntry(person);
state.ChangeState(System.Data.EntityState.Modified);
_context.SaveChanges();
return RedirectToAction("Index");
}
不引发OptimisticConcurrencyException:
另一方面,如果我从数据库中检索到我的实体的新副本,并在某些字段上进行了部分更新,然后调用SaveChanges(),那么即使存在时间戳冲突,也不会获得OptimisticConcurrencyException :
[HttpPost]
public ActionResult Index(Person person) {
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
currentPerson.Name = person.Name;
// currentPerson.VerColm == [0,0,0,0,0,0,15,167]
// person.VerColm == [0,0,0,0,0,0,15,166]
currentPerson.VerColm = person.VerColm;
// in POCO, currentPerson.VerColm == [0,0,0,0,0,0,15,166]
// in non-POCO, currentPerson.VerColm doesn't change and is still [0,0,0,0,0,0,15,167]
_context.SaveChanges();
return RedirectToAction("Index");
}
基于SQL Profiler,看来Entity Framework忽略了新的VerColm(这是timestamp属性),而是使用了最初加载的VerColm。因此,它将永远不会引发OptimisticConcurrencyException。
更新:应Jan的请求添加其他信息:
请注意,我还在上面的代码中添加了注释,以使其与在执行本示例时在 Controller 操作中看到的内容一致。
这是更新之前我的数据库中VerColm的值:0x0000000000000FA7
这是执行更新时SQL Profiler显示的内容:
exec sp_executesql N'update [dbo].[People]
set [Name] = @0
where (([Id] = @1) and ([VerColm] = @2))
select [VerColm]
from [dbo].[People]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 nvarchar(50),@1 int,@2 binary(8)',@0=N'hello',@1=1,@2=0x0000000000000FA7
请注意,@ 2应该是0x0000000000000FA6,但它是0x0000000000000FA7
更新后,这是我数据库中的VerColm:0x0000000000000FA8
有谁知道我该如何解决这个问题?我希望Entity Framework在更新现有实体时发生异常,并且存在时间戳冲突。
谢谢
最佳答案
说明
在第二个代码示例中未获得预期的OptimisticConcurrencyException
的原因是由于EF检查并发性的方式:
通过查询数据库检索实体时,EF会在查询时将所有带有ConcurrencyMode.Fixed
标记属性的值记为原始的未修改值。
然后,您更改一些属性(包括标记为Fixed
的属性)并在DataContext上调用SaveChanges()
。
EF通过将所有Fixed
标记的db列的当前值与Fixed
标记的属性的原始未修改值进行比较,检查并发更新。
这里的关键点是,EF将时间戳属性的更新视为常规数据属性的更新。您看到的行为是设计使然。
解决方案/解决方法
要解决此问题,您可以使用以下选项:
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
currentPerson.VerColm = person.VerColm; // set timestamp value
var ose = _context.ObjectStateManager.GetObjectStateEntry(currentPerson);
ose.AcceptChanges(); // pretend object is unchanged
currentPerson.Name = person.Name; // assign other data properties
_context.SaveChanges();
var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
if (currentPerson.VerColm != person.VerColm)
{
throw new OptimisticConcurrencyException();
}
currentPerson.Name = person.Name; // assign other data properties
_context.SaveChanges();
关于c# - 在某些情况下,OptimisticConcurrencyException在 Entity Framework 中不起作用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4402586/