Handling Concurrency In MVC And EF

Introduction

This article shows how to handle concurrency in MVC and EF. I am assuming that you have some idea of the Fluent API.

What is a Concurrency Issue?

It is an important issue in real-world development. The websites we develop are used by thousands of users. Concurrency occurs when two users want to modify/delete the same entity at the same time. In other words, suppose user B wants to update the entity before user A commits the change he made to that same user. If you don't handle the situation, only whoever commits last, that update will be reflected. In my applications, this risk is non-critical and okay from the requirements perspective. In that case, you need not to bother about this issue. However some projects really want to handle the concurrency issues.

What is Optimistic Concurrency?

We can implement concurrency in two ways. Pessimistic Concurrency (Locking) and Optimistic Concurrency. Pessimistic Concurrency deals with database locking and is out of context for this article since it is more specific to Windows applications. We will see what Optimistic Concurrency is.

What is Optimistic Concurrency?

"Optimistic concurrency is a concurrency method that assumes that multiple transactions can be completed without affecting each other and that, therefore, transactions can proceed without locking the data resources that they affect. Before committing, each transaction verifies that no other transaction has modified its data. If the check reveals conflicting modifications, the committing transaction rolls back."

Implementation of Concurrency

There can be various approaches available for concurrency handling. The procedure used most is known as a Store Wins scenario. In this approach, we prevent user B's change from being updated in the database. Typically user B will receive an error message showing him/her the current state of the data and allow him/her to reapply the change if he/she really wants to make the change.

Step 1. Include a tracking column that can be used to determine when the row is changed. The data type of this tracking column is generally a row version. Suppose the user writes an Update or Delete command. Then the where clause will include the original version of that column. If the row being updated has been changed by another user, the value in the row version column is different from the original value, then the statement will not find the row to update/delete and is interpreted as a concurrency conflict.

Step 2. In your model class, add a property.

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

The Timestamp attribute specifies that this column will be included in the Where clause of Update and Delete commands.

Step 3. Set the Concurrency token in the model builder.

modelBuilder.Entity<YourEntityName>()
    .Property(p => p.RowVersion)
    .IsConcurrencyToken();

Update the database from the Package Manager Console (PMC).

Step 4. Now if the concurrency conflict occurs, then the update/delete statement will generate a DbUpdateConcurrencyException. So we need to handle this exception and show the appropriate something.

catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var databaseEntry = entry.GetDatabaseValues();
    if (databaseEntry == null)
    {
        ModelState.AddModelError(string.Empty, "Unable to save changes. The entity you are trying to update was deleted by another user.");
    }
    else
    {
        ModelState.AddModelError(string.Empty, "The record you want to edit has been updated by another user. If you still want to make the change, click the 'Save' button again. Otherwise, click the back button to discard!");
        var myEntity = (MyEntity)databaseEntry.ToObject();
        MyEntity.RowVersion = myEntity.RowVersion;
    }
}

Step 5. The view will store the original RowVersion value in a hidden field, as in the following.

@Html.HiddenFor(model => model.RowVersion)

Thanks for reading. I hope you can start with this article.


Similar Articles