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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With