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
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.