Concurrency Validation using a web API not validating

Using EntityFramework v6, I am building a prototype to demonstrate concurrency validation in Web Api, as well as in a desktop application.

Essence:

public static class IRowVersionExtensions
{
    public static string RowVersionAsString(this IRowVersion ivr)
    {
        return Convert.ToBase64String(ivr.RowVersion);
    }

    public static void SetRowVersion(this IRowVersion ivr, string rowVersion)
    {
        ivr.RowVersion = Convert.FromBase64String(rowVersion);
    }
}

public interface IRowVersion
{
    byte[] RowVersion { get; set; }
}

public class Department : IRowVersion
{
    [Key]
    public int Id { get; set; }

    [Required, MaxLength(255)]
    public string Name { get; set; }

    public string Description { get; set; }

    [Timestamp]
    [ConcurrencyCheck]
    public byte[] RowVersion { get; set; }
}

Dbcontext:

public class CompDbContext : DbContextEx
{
    public CompDbContext()
        : base("Company")
    {
        this.Configuration.LazyLoadingEnabled = false;
    }

    public DbSet<Department> Departments { get; set; }
}

The desktop application (console application) has the following code and throws a DbConcurrencyException as expected: http://pastebin.com/i6yAmVGc

Now, the API controller - when I open a page in two windows and edit one (and save), then try editing / saving the other, it does not throw an exception:

Api controller update action:

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
    var original = dbContext.Departments.Find(changed.Id);
    if (original == null)
        this.NotFound();

    if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))
        Console.WriteLine("Should error.");

    original.RowVersion = changed.RowVersion;
    original.Name = changed.Name;
    original.Description = changed.Description;

    dbContext.SaveChanges();

    return original;
}

Api Call:

DepartmentVM.prototype.onSave = function (entity) {
    var method = entity.id() ? 'PATCH' : 'PUT';
    $.ajax({
        url: '/api/departments',
        method: method,
        data: ko.toJSON(entity),
        contentType: 'application/json',
        dataType: 'JSON'
    })
    .done(function (data) {
        alert('Saved');
        entity.rowVersion(data.rowVersion);
        entity.id(data.id);
    })
    .error(function (data) {
        alert('Unable to save changes to department.');
    });
};

When I break the line in the controller action:

if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))

.RowVersion == original.RowVersion() ( ). save, changed.RowVersion!= Original.RowVersion(), - , ( , ).

- , , -API?

+3
1

, EF "" RowVersion concurrency. ( DbContext) , .Find().

, , RowVersion 1, RowVersion - 2...

// changed RowVersion is 1

var original = dbContext.Departments.Find(changed.Id);
// original RowVersion is 2

if (original == null)
    this.NotFound();

if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))
    Console.WriteLine("Should error."); // 2 != 1, so prints this line


original.RowVersion = changed.RowVersion;
// original "current" RowVersion is now 1
// ... but its "original" RowVersion is still 2!

original.Name = changed.Name;
original.Description = changed.Description;

dbContext.SaveChanges();
// UPDATE DEPT SET ... WHERE Id = ... AND RowVersion = 2
// (works, therefore no concurrency exception)

, ...

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
    dbContext.Entry(changed).State = EntityState.Modified;
    dbContext.SaveChanges();

    // you'll get an exception if RowVersion has changed

    return changed;
}

, , ...

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
    dbContext.Entry(changed).State = EntityState.Unchanged;
    dbContext.Entry(changed).Property(d => d.Name).IsModified = true;
    dbContext.Entry(changed).Property(d => d.Description).IsModified = true;
    dbContext.SaveChanges();

    // you'll get an exception if RowVersion has changed

    return changed;
}

. , , Find() t1 SaveChanges() t2 ( ), .

+3

All Articles