Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to inc/dec multi user safe in entity framework 5

What is a clean, safe way to increment or decrement an integer field.

i use sql server 2012 in combination with entityframework 5.x

I look for an equivalent to interlocked increment / decrement.

like image 385
NickD Avatar asked Mar 27 '13 21:03

NickD


People also ask

How do you configure Entity Framework for optimistic concurrency?

If you do want to implement this approach to concurrency, you have to mark all non-primary-key properties in the entity you want to track concurrency for by adding the ConcurrencyCheck attribute to them. That change enables the Entity Framework to include all columns in the SQL WHERE clause of UPDATE statements.

How secure is Entity Framework?

The Entity Framework does not enforce any security permissions and will invoke any user-supplied data object code in process regardless of whether it is trusted or not. Ensure that authentication and authorization of the client is performed by the data store and by your application.


1 Answers

The standard way would be to use optimistic concurrency.

Say, you have a simple entity with an integer field Counter that you want to increment or decrement:

public class SomeEntity
{
    public int SomeEntityId { get; set; }
    public int Counter { get; set; }
}

You could mark the Counter then as a concurrency token. With Fluent API it is:

modelBuilder.Entity<SomeEntity>()
    .Property(s => s.Counter)
    .IsConcurrencyToken();

Then you can increment or decrement the Counter for example like so:

public void IncDecCounter(int someEntityId, bool dec)
{
    using (var context = new MyContext())
    {
        var someEntity = context.SomeEntities.Find(someEntityId);
        if (someEntity != null)
        {
            bool saveFailed;
            do
            {
                saveFailed = false;

                if (dec)
                    --someEntity.Counter;
                else
                    ++someEntity.Counter;

                try
                {
                    context.SaveChanges();
                }
                catch (DbUpdateConcurrencyException e)
                {
                    saveFailed = true;
                    e.Entries.Single().Reload();
                }
            } while (saveFailed);
        }
    }
}

SaveChanges will fail with a DbUpdateConcurrencyException if the value of Counter when the entity has been loaded (with Find in this example, could be any other query) differs from the value of Counter in the database when the UPDATE statement is executed in the database which would mean that the Counter has been modified by another user in the meantime. Technically this is achieved by an extended WHERE clause of the generated UPDATE statement that tries to filter not only by the Id but also by the old value of Counter, basically something like: WHERE SomeEntityId = someEntityId AND Counter = oldCounterWhenTheEntityHasBeenLoaded.

The catch block reloads the entity with the current Counter value from the database and the next loop tries to increment or decrement the reloaded value again until it succeeds without concurrency violation.

like image 193
Slauma Avatar answered Oct 26 '22 05:10

Slauma