我需要设置一个EntityObject的EntityKey。我知道它的类型和ID值。我不想不必要地查询数据库。
这行得通...
//
// POST: /Department/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
Model.EntityKey = (from Department d in db.Department
where d.Id == id
select d).FirstOrDefault().EntityKey;
db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);
db.SaveChanges();
return RedirectToAction("Index");
}
失败了...
//
// POST: /Department/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
String EntitySetName = db.DefaultContainerName + "." + Model.GetType().Name;
Model.EntityKey = new System.Data.EntityKey(EntitySetName, "Id", Model.Id);
db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);
db.SaveChanges();
return RedirectToAction("Index");
}
ApplyPropertyChanges()行因以下异常而失败:
两个EntityKeys相等。为什么第二段代码失败?我该如何解决?
最佳答案
第二个代码块失败的原因是因为EF无法在ObjectStateManager中找到对象-即,当它从数据库中拉出对象时,会将它们放在状态管理器中以便可以跟踪它们-这类似于Identity Map模式。尽管具有EntityKey,但是您的对象不在状态管理器中,因此EF无法保留更改。您可以通过将对象放入状态管理器中来解决此问题,但是对此您有些偷偷摸摸。
这有效:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department model)
{
var entitySetName = db.DefaultContainerName + "." + model.GetType().Name;
var entityKey = new System.Data.EntityKey(entitySetName, "Id", model.Id);
db.Attach(new Department{Id = id, EntityKey = entityKey});
db.AcceptAllChanges();
db.ApplyPropertyChanges(entitySetName, model);
db.SaveChanges();
}
...但是不是很干净。基本上,这是仅使用实体键附加“空”对象,接受所有更改,然后使用实际的实际更新值调用ApplyPropertyChanges。
这是扩展方法中包装的相同内容-对于使用单个db列作为主键的任何东西,此方法都应适用。调用该方法的唯一有趣的部分是,您需要告诉它如何通过委托(delegate)作为扩展方法的第二个参数来查找键属性:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department model)
{
db.ApplyDetachedPropertyChanges(model, x => x.Id);
db.SaveChanges();
}
和扩展方法:
public static class EfExtensions
{
public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity, Func<T, int> getIdDelegate)
where T : EntityObject
{
var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name;
var id = getIdDelegate(entity);
var entityKey = new EntityKey(entitySetName, "Id", id);
db.Attach(new Department {Id = id, EntityKey = entityKey});
db.AcceptAllChanges();
db.ApplyPropertyChanges(entitySetName, entity);
}
}
由于扩展方法正在调用AcceptAllChanges,因此如果您要一次在多个实体上进行更新,则需要谨慎地进行调用-如果不小心,很容易“丢失”更新。因此,此方法仅真正适用于简单的更新方案-例如许多MVC Action 方法:)