私は、リポジトリ パターンで EF CodeFirst を使用し、DbUpdateConcurrencyException をテストしてキャッチしようとしている asp.net mvc アプリケーションを持っていますが、1 つのブラウザーでレコードを更新するときでも、夕方には起動していないようです。がすでに別の場所で開かれていて、別の場所に移動して同じレコードを更新しようとすると、それが可能になります。テーブル (RowVersion と呼ばれる) に TimeStamp 列があり、これは更新ごとに変更されます。これは正しいことです。SQL プロファイラを監視すると、更新ステートメントがタイムスタンプが渡されたものであることを確認していることがわかります。私はアイデアがなく、これまで本当に私を助けるものを見つけることができません. 誰かが私が間違っていることを見ることができたら、私に知らせてください。ありがとう!!
ここに私のエンティティがあります:
public class MyEntity : EntityBase
{
public override int Id { get; set; }
public DateTime? LastEdited { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
これがリポジトリであり、その基本クラス/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);
}
これが私のコントローラーアクションです:
[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() });
}