我们的项目目前正在迁移到EF(远离Stored Procs),其中一个增强功能(我们正在添加到架构中)是在用户将数据保存到数据库时使用Optimistic Concurrency(我们目前还没有此功能)。我遇到问题时遇到EF失败的问题。换句话说,当两个用户打开相同的记录时,每个用户都进行更改并尝试保存这些更改,第一个用于保存更新记录,第二个用户将收到错误消息。我创建了一个简单的例子来说明我的问题。
在数据库中,我有下表(并插入测试数据):
Create Table Work
(
Id int identity(1,1) Primary Key
,UserIdAssignTo int null
,RowVer RowVersion not null
)
Insert Into Work(UserIdAssignTo)Values(1)
我创建了一个EF文件(.edmx)并将上面的表拖放到画布上。我更新了属性/列RowVer上的属性,如下所示:
我有一个对象将检索和更新表格,如下所示:
public class Work
{
public int Id { get; set; }
public int? UserIdAssignTo { get; set; }
public byte[] Version { get; set; }
private string _conn = String.Empty;
public WorkData()
{
_conn = GetConnectionsString();
}
public void GetById(int WorkID)
{
using (SQL context = new SQL(_conn))
{
Work fromDb = context.Works.FirstOrDefault(db => db.Id == WorkID);
if (fromDb != null)
{
Id = fromDb.Id;
UserIdAssignTo = fromDb.UserIdAssignTo;
Version = fromDb.RowVer;
}
}
}
public void Update()
{
using (SQL context = new SQL(_conn))
{
Work fromDb = context.Works.FirstOrDefault(db => db.Id == Id);
if (fromDb != null)
{
fromDb.UserIdAssignTo = UserIdAssignTo;
fromDb.RowVer = Version;
context.SaveChanges();
UserIdAssignTo = fromDb.UserIdAssignTo;
Version = fromDb.RowVer;
}
}
}
}
我开发了一个测试用例来揭示我得到的错误:
[Test]
public void ConcurencyDataTest()
{
WorkData first = new WorkData();
first.GetById(1);
WorkData second = new WorkData();
second.GetById(1);
first.UserIdAssignTo = null;
first.Update();
second.UserIdAssignTo = 1;
second.Update(); // I should get an exception b/c the object is outdated
}
在“first”和“second”对象调用GetById(1)方法之后,它们的RowVer属性对于两个对象都是相同的(如预期的那样)。
我执行此测试时运行了SQL分析器
以下是“第一个”对象称为Update方法
exec sp_executesql N'update [dbo].[Work]
set [UserIdAssignTo] = null
where (([Id] = @0) and ([RowVer] = @1))
select [RowVer]
from [dbo].[Work]
where @@ROWCOUNT > 0 and [Id] = @0',N'@0 int,@1 binary(8)',@0=1,@1=0x00000000024E6E2
注意@ 1参数,“第一个”和“第二个”对象都应该在内存中使用它并在更新时使用它
当调用second.Update时,SQL分析器记录了这个:
exec sp_executesql N'update [dbo].[Work]
set [UserIdAssignTo] = @0
where (([Id] = @1) and ([RowVer] = @2))
select [RowVer]
from [dbo].[Work]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 int,@1 int,@2 binary(8)',@0=1,@1=1,@2=0x00000000024E6E2F
注意@ 1参数已更改为新值(在“first”更新之后),此时它应该是对象“second”(旧值为0x00000000024E6E2)所持有的旧值。我不明白它是如何改变的,我对如何通过EF正确实现第一次写并发感到困惑。
我实际得到的结果是“第二个”对象成功更新表,当它应该失败时。
编辑:这是为了模拟使用N层架构。我正在尝试使用分离的对象进行更新。
答案 0 :(得分:2)
我认为这是因为在您的更新方法中,您将从上下文中再次检索该对象,该上下文将获得RowVer的当前值。由于它是计算的,我不认为将它设置回以前的版本会起作用。因此,当它更新时,它确实具有表中的RowVer的当前值。
我认为您需要将对象附加或添加到上下文中。