How do you use optimistic concurrency with WebAPI OData controller


Question

I've got a WebAPI OData controller which is using the Delta to do partial updates of my entity.

In my entity framework model I've got a Version field. This is a rowversion in the SQL Server database and is mapped to a byte array in Entity Framework with its concurrency mode set to Fixed (it's using database first).

I'm using fiddler to send back a partial update using a stale value for the Version field. I load the current record from my context and then I patch my changed fields over the top which changes the values in the Version column without throwing an error and then when I save changes on my context everything is saved without error. Obviously this is expected, the entity which is being saved has not been detacched from the context so how can I implement optimistic concurrency with a Delta.

I'm using the very latest versions of everything (or was just before christmas) so Entity Framework 6.0.1 and OData 5.6.0

public IHttpActionResult Put([FromODataUri]int key, [FromBody]Delta<Job> delta)
{
    using (var tran = new TransactionScope())
    {
        Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);

        delta.Patch(j);

        this._context.SaveChanges();

        tran.Complete();

        return Ok(j);
    }
}

Thanks

Popular Answer

I've just come across this too using Entity Framework 6 and Web API 2 OData controllers.

The EF DbContext seems to use the original value of the timestamp obtained when the entity was loaded at the start of the PUT/PATCH methods for the concurrency check when the subsequent update takes place.

Updating the current value of the timestamp to a value different to that in the database before saving changes does not result in a concurrency error.

I've found you can "fix" this behaviour by forcing the original value of the timestamp to be that of the current in the context.

For example, you can do this by overriding SaveChanges on the context, e.g.:

public partial class DataContext
{
    public override int SaveChanges()
    {
        foreach (DbEntityEntry<Job> entry in ChangeTracker.Entries<Job>().Where(u => u.State == EntityState.Modified))
            entry.Property("Timestamp").OriginalValue = entry.Property("Timestamp").CurrentValue;

        return base.SaveChanges();
    }
}

(Assuming the concurrency column is named "Timestamp" and the concurrency mode for this column is set to "Fixed" in the EDMX)

A further improvement to this would be to write and apply a custom interface to all your models requiring this fix and just replace "Job" with the interface in the code above.

Feedback from Rowan in the Entity Framework Team (4th August 2015):

This is by design. In some cases it is perfectly valid to update a concurrency token, in which case we need the current value to hold the value it should be set to and the original value to contain the value we should check against. For example, you could configure Person.LastName as a concurrency token. This is one of the downsides of the "query and update" pattern being used in this action.

The logic you added to set the correct original value is the right approach to use in this scenario.





Licensed under: CC-BY-SA
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why