Sometimes we want to make sure that the user is working with the latest version of the data. For example, if the user is viewing a record from our database in his screen, and then decides to update that record, then we want to make sure that, during the period of viewing and updating, the data that the user is viewing has not been modified. We want that if there was such modification then an error is thrown, since we do not want the user to work with outdated data. We can achieve this with the ConcurrencyCheck attribute.
This can be important due to data integrity situations, because, if during the update the record was updated by another user, then data could be lost.
Let’s see the following example. Suppose we have a student in the database with the name Felipe, and that we have the following code:
using (var context = new ApplicationDbContext()) { var student = context.Students.First(x => x.Name == "Felipe"); student.Name += " 2"; context.SaveChanges(); }
This code searches for a student named Felipe, and updates his name by adding a “2” at the end. Suppose we have a breakpoint in SaveChanges, so that when we run the code, the execution will stop before making the changes in the database. Now, while the execution is stopped, I can go to the database and change the name of “Felipe” to “Felipe 1”. After making this change, I continue with the execution of the program.
As a result of the above, the name “Felipe” is now “Felipe 2” in the database. What happened to “Felipe 1”? Well that change was lost, which may not be what you want. In a slightly more serious situation, as in the case of banking transactions, this is something you certainly would not want. Imagine that to an account that has a balance of $500, we do two simultaneous debits of $500, then the bank would be giving $500 extra, which should not be the case.
As I was saying, to counteract this situation, what we should do is use the ConcurrencyCheck attribute on the Name attribute, like this:
[ConcurrencyCheck] public string Name { get; set; }
Or you can do the configuration via the Fluent API:
modelBuilder.Entity<Student>() .Property(x => x.Name).IsConcurrencyToken(true);
Now, if we go repeat the previous experiment, we will see that we get an error of type Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException, which indicates that the user was not working with the latest version of the data.
Summary
Use the ConcurrencyCheck attribute or the IsConcurrencyToken function to make sure that the user is always working with the latest version of the data.