4

我有一个代码优先 EF 模型,在某些实体上带有并发令牌。这些标记被定义为 byte[] 属性并用 [Timestamp] 属性修饰。

[Timestamp]
public byte[] ConcurrencyStamp { get; set; }

(我也尝试过Timestampand的任意组合ConcurrencyCheck

OnModelCreating在我的上下文中,这些属性也被标记为并发标记:

modelBuilder.Entity<Room>().Property(x => x.ConcurrencyStamp).IsConcurrencyToken();

所以,这里的场景:

一些实体被序列化为 JSON 并传递给外部客户端。当客户端更新一个对象时,该更改的对象会再次被接收(作为 Json)并将更改应用于新获取的对象。在这个过程中,我还将从客户端接收到的 concurrencytoken 值更新为刚刚从 db 中获取的对象。然后,在保存更改时,即使值不匹配,也不会引发并发错误。

So, to summarize:
1. fetch object from DB
2. serialize object to JSON (including concurrencytoken)
3. client messes with object
4. server receives updated object as json 
5. fetch object (by id) from DB
6. apply json values to fetched object (including concurrencytoken)
7. context.savechanges
--> no error if token was changed

检查日志,似乎 EF 在保存更改时使用“获取的”并发令牌执行更新语句,而不是从外部对象手动设置的令牌。

UPDATE [dbo].[Rooms]
SET [RoomName] = @0, [ConcurrencyStamp] = @1
WHERE (([RoomId] = @2) AND ([ConcurrencyStamp] = @3))

-- @0: 'new room name' (Type = String, Size = -1)
-- @1: '1500' (Type = Int64)
-- @2: '1' (Type = Int32)
-- @3: '1999' (Type = Int64)

(我在这里使用了 long,但同样适用于我最初尝试过的 byte[] 标记)。

1999是数据库中的当前并发令牌值。1500是来自 JSON 对象的令牌,通过设置属性手动设置。

即使您可以看到 EF 在语句中更新令牌(因为我设置了属性),它仍然使用原始令牌值进行检查。

通过更改跟踪器更改属性没有帮助,行为保持不变。有什么线索吗?不支持这种情况吗?难道我做错了什么?

更新

检查确实有效。当在单独的线程中创建新上下文并在 fetch 和 savechanges 之间进行更改(因此在步骤 5 和步骤 7 之间)时,步骤 7 中的 savechanges 与ConcurrencyException.

所以它看起来像描述的那样工作,但是没有办法“强制”令牌在外部更新(我猜这在某种程度上可能是有意义的)。

4

2 回答 2

3

你实际上可以强制它。您只需要像这样设置时间戳:

   customerRequest.RowVersion = detachedRequest.RowVersion;

   Context.Entry(customerRequest).Property(p => p.RowVersion).OriginalValue = customerRequest.RowVersion;

   Context.Entry(customerRequest).Property(p => p.RowVersion).IsModified = false;

之后 ef 会认为它没有更新,并会在更新时抛出并发异常。

首先在 ef 6 代码上进行测试。

于 2013-12-09T13:57:10.763 回答
2

EF 总是在步骤 7 的语句中使用OriginalValue在步骤 5 中获取的时间戳。UPDATE

步骤 6 中的设置entity.ConcurrencyStamp = viewModel.ConcurrencyStamp仅更新CurrentValue.

要设置OriginalValue,请在步骤 6 中执行此操作:

dbContext.Entry(entity).Property(e => e.ConcurrencyStamp).OriginalValue =
    viewModel.ConcurrencyStamp;
于 2014-04-03T14:16:55.473 回答