EntityKey和ApplyPropertyChanges()

我需要设置一个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()行因此exception而失败:

ObjectStateManager不包含ObjectStateEntry,它引用了“Sample.Models.Department”类型的对象。

两个EntityKeys是平等的。 为什么第二块代码失败? 我该如何解决?

你的第二个代码块失败的原因是因为EF无法在ObjectStateManager中找到对象 – 即当它从db中提取对象时它将它们放在状态管理器中以便它可以跟踪它们 – 这类似于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列的任何内容。 调用该方法唯一有趣的部分是你需要告诉它如何通过委托找到key属性作为扩展方法的第二个参数:

 [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(this ObjectContext db, T entity, Func 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动作方法:)

 public static class EfExtensions { public static void ApplyDetachedPropertyChanges(this ObjectContext db, T entity, Func getIdDelegate) where T : EntityObject { var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name; T newEntity = Activator.CreateInstance(); newEntity.EntityKey = db.CreateEntityKey(entitySetName, entity); Type t = typeof(T); foreach(EntityKeyMember keyMember in newEntity.EntityKey.EntityKeyValues) { PropertyInfo p = t.GetProperty(keyMember.Key); p.SetValue(newEntity, keyMember.Value, null); } db.Attach(newEntity); //db.AcceptAllChanges(); db.ApplyPropertyChanges(entitySetName, entity); } } 

尝试使用下面的代码,让我知道它是否适合您。

 [AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(Guid id, Department Model) { using (var context = new EntityContext()) { try { Object entity = null; IEnumerable> entityKeyValues = new KeyValuePair[] { new KeyValuePair("DepartmentID", id) }; // Create the key for a specific SalesOrderHeader object. EntityKey key = new EntityKey("EntityContext.Deparment", entityKeyValues); // Get the object from the context or the persisted store by its key. if (context.TryGetObjectByKey(key, out entity)) { context.ApplyPropertyChanges(key.EntitySetName, Model); context.SaveChanges(); } else { // log message if we need //"An object with this key could not be found." } } catch (EntitySqlException ex) { // log message } } } 

改进史蒂夫威尔科克的伟大实施,这是我的建议。

它比原始示例更多地使用Reflection(.NET的一部分),以节省一些代码。 它还自动支持任何类型的实体类,而不仅仅是“部门”。

此外,它摆脱了过时的ApplyPropertyChanges方法,并使用新的ApplyCurrentValues方法。

方法

该方法基本上只使用reflection来动态获取“Id”属性的值,并进行设置。 这节省了代表的所有麻烦。

 public static void ApplyDetachedPropertyChanges(this ObjectContext db, T entity) where T : EntityObject { PropertyInfo idProperty = typeof(T).GetProperty("Id"); var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name; var id = idProperty.GetValue(entity, null); var entityKey = new EntityKey(entitySetName, "Id", id); Type type = entity.GetType(); EntityObject obj = (EntityObject)Activator.CreateInstance(type); idProperty.SetValue(obj, id, null); obj.EntityKey = entityKey; db.Attach(obj); db.AcceptAllChanges(); db.ApplyCurrentValues(entitySetName, entity); } 

用法

使用它也很简单。

 [AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(Guid id, Department Model) { db.ApplyDetachedPropertyChanges(Model); db.SaveChanges(); } 

我有同样的问题,而它正在其他页面上工作。 我不知道这条评论是否有用……但我终于发现,在我的对象(相当于你的部门)中,我的“Id”没有出现在表格中……

只是在我的表单上添加它(当我删除了它…)的事实解决了我的问题。 (使用style =“visibility:hidden”,为了不看他……)