user1368182
user1368182

Reputation: 473

asp.net mvc ef codefirst repository concurrency not working

I have an asp.net mvc application where I use EF CodeFirst with a repository pattern and am trying to test and catch a DbUpdateConcurrencyException, but it doesn't appear to be firing, evening though when I update a record in one browser, while it is already open in another, and then go to the other and try to update the same record, it allows me to do it. I have a TimeStamp column on the table (called RowVersion) which does change with every update, so that is correct, and from watching sql profiler I can see the update statement does check that the timestamp is what is being passed in, so I'm out of ideas, and am unable to find anything so far to really help me. If anyone can see something I am doing wrong, please let me know. Thanks!!

Here is my entity:

public class MyEntity : EntityBase
{
    public override int Id { get; set; }

    public DateTime? LastEdited { get; set; }

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

Here is the repository and it's base classes/dbcontext:

public class MyEntityDataProvider : EfDataProviderBase<MyEntity>, IMyEntityDataProvider
{
    public MyEntityDataProvider (IDataContext dataContext) : base(dataContext) { }
}

public abstract class EfDataProviderBase<T> : DataProviderBase<T> where T : EntityBase
{
    public new DbDataContext DataContext
    { 
        get { return base.DataContext as DbDataContext; }
        protected set { base.DataContext = value; }
    }

    public EfDataProviderBase(IDataContext dataContext)
        : base(dataContext)
    {
        if (!(dataContext is DbDataContext)) throw new ArgumentException("Parameter 'dataContext' must be of type DbDataContext.");
    }

    public override T GetById(int id)
    {
        if (id < 1) return null;

        return this.DataContext.GetDbSet<T>().Find(id);
    }

    public override List<T> GetAll()
    {
        return this.DataContext.GetDbSet<T>().OrderBy(e => e.Id).ToList();
    }      

    public override void Insert(T entity)
    {
        if (entity == null) throw new ArgumentNullException("entity");

        this.DataContext.GetDbSet<T>().Add(entity);
    }

    public override void Update(T entity)
    {
        if (entity == null) throw new ArgumentNullException("entity");

        if (!this.DataContext.GetDbSet<T>().Local.Any(e => e.Id == entity.Id))
            this.DataContext.GetDbSet<T>().Attach(entity);
        this.DataContext.Entry(entity).State = EntityState.Modified;
    }
}

public class DbDataContext : DbContext, IDataContext
{
    public DbSet<MyEntity> MyEntities { get; set; }

    public DbSet<T> GetDbSet<T>() where T : EntityBase
    {
        Type entityType = typeof(T);

        if (entityType == typeof(User))
            return this.Users as DbSet<T>;
    }


     public void Commit()
     {
        this.SaveChanges();
     }
 }

public abstract class DataProviderBase<T> : IDataProvider<T> where T: EntityBase
{
    public IDataContext DataContext { get; protected set; }

    public DataProviderBase(IDataContext dataContext)
    {
        if (dataContext == null) throw new ArgumentNullException("dataContext");

        this.DataContext = dataContext;
    }


    public abstract T GetById(int id);

    public abstract List<T> GetAll();

    public void Save(T entity)
    {
        if (entity == null) throw new ArgumentNullException("entity");

        if (entity.IsNew)
        {
            this.Insert(entity);
        }
        else if (entity.IsDeleted)
        {
            this.Delete(entity);
        }
        else
        {
            this.Update(entity);
        }
    }

    public abstract void Insert(T entity);

    public abstract void Update(T entity);
}

Here is my controller action:

   [HttpPost]
    [ValidateAntiForgeryToken()]
    public ActionResult UpdateViewModel(ViewModel model)
    {
        if (ModelState.IsValid)
        {
            try
            {
                MyEntity current = this.MyEntityDataProvider.GetById(model.Id);

                current.LastEdited = DateTime.UtcNow;
                current.RowVersion = model.Timestamp; // should have original timestamp, getting from hidden field on view

                this.MyEntityDataProvider.Save(current);
                this.DataContext.Commit(); // one dbcontext per http request, injected by ninject

                return Json(new { success = "Saved!" });
            }
            catch (DbUpdateConcurrencyException)
            {
                ModelState.AddModelError("", "This record has been edited by an another person since you have opened it - please close and re-open to attempt your changes again.");
            }
        }

        return Json(new { errors = GetErrorsFromModelState() });
    }

Upvotes: 0

Views: 815

Answers (1)

Luis Henrique
Luis Henrique

Reputation: 11

Good morning friend. You should add information on the property you want to control concurrency.

Example:

[ConcurrencyCheck]
     public int Code {get; set;}

Upvotes: 1

Related Questions