Jasinski Technical Wiki

Navigation

Home Page
Index
All Pages

Quick Search
»
Advanced Search »

Contributor Links

Create a new Page
Administration
File Management
Login/Logout
Your Profile

Other Wiki Sections

Software

PoweredBy

Page History: Concurrency Exceptions - Entity Framework 6 and EF Core

Compare Page Revisions



« Older Revision - Back to Page History - Newer Revision »


Page Revision: Wed, Aug 26, 2020, 2:25 PM


Overview

This article outlines the steps to handle concurrency exceptions in Entity Framework 6.

Concurrency exceptions happen only on entities/tables that implement a row version field. They occur when EF detects that an underlying data record has changed since it was first retrieved. This frequently happens because two processes or users are trying to edit the record simultaneously. (Note that a concurrency exception is only relevant for updates — not inserts or deletes.)

References


Implementation

Data Model

The entity/table that we want to implement optimistic concurrency on must have a field similar to the following.

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

Exception Handling and Retry Logic

var done = false;
var iterNum = 1;
var maxIterations = 5;

do
{
    try
    {

        LoggingEngine.Info($"Starting Iteration {iterNum}.");

        /*  TODO: Implement logic to update record. */

        db.SaveChanges();

        done = true;
    }
    catch (SqlException ex) when (ex.Number == 1205) // 1205 = deadlock victim
    {
        iterNum++;
        done = (iterNum >= maxIterations);
        if (done)
        {
            LoggingEngine.Exception($"Deadlock exception while trying to "
                + $"do stuff - iteration {iterNum}. EXITING!", ex);
        }
        else
        {
            LoggingEngine.Exception($"Deadlock exception while trying to "
                + $"do stuff - iteration {iterNum}. Retrying operation.", ex);
        }
    }
    catch (DbUpdateConcurrencyException ex)
    {
        iterNum++;
        done = (iterNum > maxIterations);

        if (done)
        {
            LoggingEngine.Exception($"Concurrency exception while trying to " 
                + $"do stuff - iteration {iterNum-1}. EXITING!", ex);
        }
        else
        {
            LoggingEngine.Exception($"Concurrency exception while trying to "
                + $"do stuff - iteration {iterNum-1}. Retrying.", ex);

            foreach (var en in ex.Entries)
            {
                en.Reload(); // RELOAD = database wins
            }
        }
    }
    catch (Exception ex)
    {
        done = true;
        LoggingEngine.Exception($"Exception while trying to do stuff", ex);
    }
} while (!done);

Useful Functions

The following functions are useful to convert the RowVersion field above to/from a 64-bit integer. This can easily be passed to/from the front end code for the use case where the user needs to resolve the conflict. (Note: This is written in C#.NET Core.)

private long ByteArrayToLong(byte[] input)
{
    if (input.Length > 8)
    {
        throw new InvalidOperationException("Byte array can have no more than 8 elements.");
    }

    var result = BitConverter.ToInt64(input.Reverse().ToArray(), 0);

    return result;
}
private byte[] LongToByteArray(long input)
{
    var result = BitConverter.GetBytes(input).Reverse().ToArray();
    return result;
}

ScrewTurn Wiki version 3.0.1.400. Some of the icons created by FamFamFam. Except where noted, all contents Copyright © 1999-2024, Patrick Jasinski.