AspNetUsers表中的ConcurrencyStamp列在新的ASP.NET MVC 6身份验证中有什么作用?

103
ConcurrencyStamp列在新的ASP.NET MVC 6身份验证中的AspNetUsers表中的目的是什么?
这是AspNetUsers表的数据库模式:

enter image description here

它也存在于AspNetRoles表中:

enter image description here

据我记得,在ASP.NET MVC 5身份验证中那里没有它。

目前我注意到的是,它似乎具有GUID值,因为它是用以下代码定义的:

/// <summary>
/// A random value that must change whenever a user is persisted to the store
/// </summary>
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();

但是这份文档并不足以让我理解它在哪些情况下被使用。


3
我会假设(还没有使用v6),正如其名称所示,它用于处理对“用户”并发更新。我手动添加类似的东西(例如rowversion/ timestamp)来实现相同的功能。希望这能帮到你... - EdSF
我开始认为这是用于 ASP.NET 缓存目的。 - Nikolay Kostov
1
我想知道的是,如果它不会比GUID更长,为什么他们将其设置为nvarchar(MAX) - MikeT
@MichaelTranchida 跨平台/数据库,我猜。此外,您甚至可以将其存储在不是 SQL 数据库的地方。 - Simon_Weaver
4个回答

136
如其名,它用于防止并发更新冲突。
例如,数据库中有一个名为Peter的用户UserA,有两个管理员打开了UserA的编辑页面,想要更新这个用户。
1. Admin_1打开了页面,看到了名为Peter的用户。 2. Admin_2打开了页面,也看到了名为Peter的用户(显然)。 3. Admin_1将用户名更新为Tom,并保存数据。现在数据库中的UserA被命名为Tom。 4. Admin_2将用户名更新为Thomas,并尝试保存。
如果没有并发标记(ConcurrencyStamp),那么Admin_1的更新将被Admin_2的更新覆盖。 但是由于我们有了并发标记(ConcurrencyStamp),当Admin_1/Admin_2加载页面时,标记会被加载。在更新数据时,这个标记也会被更改。 因此,现在第5步将会是系统抛出异常,告诉Admin_2该用户已经被更新,因为他加载的并发标记与当前不同。

1
如何在控制器中检查此戳记?例如,在编辑控制器中,我会编写database.id==model.id && database.stamp==model.stamp,然后更新,否则出错。这是检查它的方法吗?请确认,谢谢。 - Abubakar Riaz
不知道他们是否可以使用“前提条件失败”HTTP状态来实现这个。 - Beginner
@AbubakarRiaz 你不需要手动检查它。在更新数据库条目时,EF会自动检查它。 - Steven.Xi
1
@初学者 不是关于在哪里检查的问题,而是关于竞态条件的问题。 - Steven.Xi

27

从源代码本身

    /// <summary>
    /// A random value that should change whenever a role is persisted to the store
    /// </summary>
    public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();

基本上,将其视为所命名的那样。这是一个用于标识数据当前版本的印章。如果您更改它,那么该印戳也会更改。

因此,如果同时出现两个并发更新,则它们必须具有相同的印戳,否则其中之一应被丢弃。

因此得名ConcurrencyStamp


添加了更多信息。抱歉,缺乏咖啡因。 - Maxime Rouiller
3
“两个并发的更新...必须拥有相同的时间戳”是不正确/不完整的。任何更新必须与存储行具有相同的时间戳。 - H H

16

关于 Maxime 的回复,我想补充一点:

如果你查看 OnModelCreating() 方法中 IdentityDbContext 的实现,你会发现:

builder.Entity<TUser>(b =>
{
....
    b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
....

以及在 UserStore.UpdateAsync(...) 方法中:

    Context.Update(user);
    try
    {
        await SaveChanges(cancellationToken);
    }
    catch (DbUpdateConcurrencyException)
    {
        return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
    }

因此,它确实做到了它应该做的事情:防止并发更新用户对象。令牌仅在ASP身份EntityFramework模块中“在幕后”使用。基本上,如果一个用户对象的并发更新发生,数据库上下文将抛出DbUpdateConcurrencyException异常。


10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接