Reputation: 9818
I am using EF 6.1 and my service update method looks like so
public async override Task<int> UpdateAsync(Module updated)
{
var entity = await _context.Modules
.Where(e => e.Id == updated.Id)
.FirstOrDefaultAsync();
// update existing values
_context.Entry(entity).CurrentValues.SetValues(updated);
// do not update
_context.SetModified(entity, "AddOnlyProperty", false);
return await base.UpdateAsync(entity);
}
My base UpdateAsync looks like so
public async virtual Task<int> UpdateAsync(T updated)
{
var results = await ValidateAsync(updated);
if (!results.IsValid)
{
throw new ValidationException(results.Errors);
}
if (_context.GetState(updated) == EntityState.Detached)
{
_context.Set<T>().Attach(updated);
_context.SetState(updated, EntityState.Modified);
}
// we do not want the createdDate to change when an update is being done
_context.SetModified(updated, "CreatedDate", false);
_context.SetModified(updated, "CreatedByUserId", false);
return await _context.SaveChangesAsync();
}
This method is very simple, it copies the changes from updated into the entity, that way EF will only apply the changes needed when it creates the SQL.
My problem is that concurrency doesnt seem to be working, i would expect a DbConcurrencyException to be thrown?
Here is my basic Module object
public class Module : EntityBase, IEntityVersion, IEntityDelete
{
public long Id { get; set; }
public string Name { get; set; }
public byte[] RowVersion { get; set; }
}
I have configured RowVersion as a rowVersion field in my Module.config file, like so
this.Property(e => e.RowVersion)
.IsRowVersion();
Now when i perform an update with an old rowVersion value, the update seems to still take place? Have i missed a configuration setting or am i doing this wrong?
Should an exception be thrown, or should i be checking if the rowVersion values match myself?
Upvotes: 3
Views: 959
Reputation: 211
You must add this to force ef to add condition for checking RowVersion on update.
dbContext.Entry(entity).OriginalValues["RowVersion "] = yourCurrentRowVersion;
Upvotes: 2
Reputation:
You retrieve the record.
You then modify the record.
Someone else modifies the record too, and saves the change.
You retrieve the record again, and update the values to match your desired values.
You save your changes.
The problem here is the "You retrieve the record again" step. Because you do this after the other person saved the changes, you get the new row version value already. This is the step you need to skip.
One way of handling it is using one Context
throughout the process, and leaving the entity-to-be-modified attached to the context. Entity Framework will be able to keep track of the original values.
Another way of handling it is keeping track of the original values yourself. When you want to save the changes, create a detached entity, set the properties to the original values, attach it to the context, and then set the properties to the modified values.
Actually, thinking about it some more, instead of using on EF's concurrency handling, you should be able to trivially code your own here:
var entity = await _context.Modules
.Where(e => e.Id == updated.Id)
.FirstOrDefaultAsync();
if (entity == null) {
... // throw exception about record not existing in the database
}
if (BitConverter.ToInt64(entity.RowVersion, 0) != BitConverter.ToInt64(updated.RowVersion, 0)) {
... // throw exception about record having been updated in the database
}
... // your existing code to update the entity can go here
Upvotes: 2