101

What is the purpose of the ConcurrencyStamp column in the AspNetUsers table in the new ASP.NET MVC 6 identity?

This is the database schema of the AspNetUsers table:

enter image description here

It is also there in the AspNetRoles table:

enter image description here

As I remember it wasn't there in the ASP.NET MVC 5 identity.

What I've noticed so far is that it seems to have GUID values as it is defined with the following code:

/// <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();

But this documentation is not sufficient for me to understand in which situations it is used.

Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
  • 3
    I will _assume_ (haven't used v6 yet), that as the name implies it is used to handle concurrent `updates` to a `user`. I _manually_ add something similar (e.g. `rowversion` / `timestamp`) to [accomplish same](http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application). Hth... – EdSF Dec 13 '15 at 17:13
  • I am starting to thing that this is for ASP.NET caching purposes. – Nikolay Kostov Dec 14 '15 at 17:23
  • 1
    What I want to know is, why did they set it to `nvarchar(MAX)` if it's gonna be no longer than a GUID – MikeT Jan 17 '18 at 01:25
  • @MichaelTranchida cross platform/database I'm guessing. Plus you could store it somewhere that isn't even a SQL database. – Simon_Weaver Dec 01 '18 at 09:08

4 Answers4

134

As the name state, it's used to prevent concurrency update conflict.

For example, there's a UserA named Peter in the database 2 admins open the editor page of UserA, want to update this user.

  1. Admin_1 opened the page, and saw user called Peter.
  2. Admin_2 opened the page, and saw user called Peter (obviously).
  3. Admin_1 updated user name to Tom, and save data. Now UserA in the db named Tom.
  4. Admin_2 updated user name to Thomas, and try to save it.

What would happen if there's no ConcurrencyStamp is Admin_1's update will be overwritten by Admin_2's update. But since we have ConcurrencyStamp, when Admin_1/Admin_2 loads the page, the stamp is loaded. When updating data this stamp will be changed too. So now step 5 would be system throw exception telling Admin_2 that this user has already been updated, since he ConcurrencyStamp is different from the one he loaded.

Steven.Xi
  • 1,670
  • 1
  • 13
  • 15
  • 1
    how would one check this stamp in controller? like in edit controller i would write database.id==model.id && database.stamp==model.stamp then update else error. is this the way to check it? please confirm thank you – Abubakar Riaz Apr 11 '20 at 16:21
  • Wonder if they could've used the Precondition Failed http status to achieve this – Beginner Aug 28 '22 at 14:54
  • @AbubakarRiaz you don't need to check it manually. It's checked by the EF when updating database entry. – Steven.Xi Aug 29 '22 at 15:03
  • 1
    @Beginner no... this is for the racing condition, not related to where to check. – Steven.Xi Aug 29 '22 at 15:03
25

From the source code itself

    /// <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();

Basically, see it as it is named. A stamp that is used to identify the current version of the data. If you change it, so does the stamp.

So if two concurrent updates comes in at the same time, they must have the same stamp or one of them should be discarded.

Hence the name, ConcurrencyStamp.

Maxime Rouiller
  • 13,614
  • 9
  • 57
  • 107
15

To follow up on Maxime's reply:

If you look at the implementation of IdentityDbContext in the OnModelCreating() method, you'll find:

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

and in the UserStore.UpdateAsync(...)-method:

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

So, it indeed does what it's supposed to do: prevent concurrent updates on a user object. The token is merely used "under the hood" in the ASP Identity EntityFramework module. Basically, if a concurrent update of one user object occurs, the DB context throws DbUpdateConcurrencyException.

mcb
  • 194
  • 2
  • 8
10

Also important to realize that this is actually a data layer feature. The schema for Identity defines the column as being a concurrency column but doesn't actually use it.

There's no logic going on inside the Identity codebase - it's only when EFCore actually goes to save it that it kicks in.

https://learn.microsoft.com/en-us/ef/core/modeling/concurrency

EF Code First - IsConcurrencyToken()

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689