C# EntityKey和ApplyPropertyChanges()

C# EntityKey和ApplyPropertyChanges(),c#,.net,asp.net-mvc,entity-framework,C#,.net,Asp.net Mvc,Entity Framework,我需要设置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

我需要设置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()行失败,出现以下异常:

ObjectStateManager不支持 包含带有 对类型为的对象的引用 “样本、模型、部门”


这两个实体键相等。为什么第二块代码会失败?如何修复它?

您的第二个代码块失败的原因是EF在ObjectStateManager中找不到对象-也就是说,当它从db中提取对象时,它会将它们放在状态管理器中以便跟踪它们-这与模式类似。尽管有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<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);
  }
}
以及扩展方法:

[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);
  }
}
公共静态类扩展
{
公共静态void ApplyDetailedPropertyChanges(此ObjectContext数据库、T实体、Func getIdDelegate)
其中T:EntityObject
{
var entitySetName=db.DefaultContainerName+“+entity.GetType().Name;
var id=getIdDelegate(实体);
var entityKey=新的entityKey(entitySetName,“Id”,Id);
附加(新部门{Id=Id,EntityKey=EntityKey});
db.acceptablechanges();
db.ApplyPropertyChanges(entitySetName,entity);
}
}
由于扩展方法正在调用AcceptAllChanges,如果您同时对多个实体进行更新,则需要小心调用此方法-如果不小心,很容易“丢失”更新。因此,这种方法只适用于简单的更新场景-例如,许多MVC操作方法:)

公共静态类扩展
{
公共静态void ApplyDetailedPropertyChanges(此ObjectContext数据库、T实体、Func getIdDelegate)
其中T:EntityObject
{
var entitySetName=db.DefaultContainerName+“+entity.GetType().Name;
T newEntity=Activator.CreateInstance();
newEntity.EntityKey=db.CreateEntityKey(entitySetName,entity);
类型t=类型(t);
foreach(newEntity.EntityKey.EntityKeyValues中的EntityKeyMember-keyMember){
PropertyInfo p=t.GetProperty(keyMember.Key);
p、 SetValue(newEntity,keyMember.Value,null);
}
db.附件(新实体);
//db.acceptablechanges();
db.ApplyPropertyChanges(entitySetName,entity);
}
}

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

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
    using (var context =  new EntityContext())
    {
        try
        {
            Object entity = null;
            IEnumerable<KeyValuePair<string, object>> entityKeyValues =
                new KeyValuePair<string, object>[] {
                    new KeyValuePair<string, object>("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
        }
    }
 }       
[AcceptVerbs(HttpVerbs.Post)]
公共操作结果编辑(Guid id,部门模型)
{
使用(var context=new EntityContext())
{
尝试
{
对象实体=null;
IEnumerable entityKeyValues=
新的KeyValuePair[]{
新的KeyValuePair(“部门id”,id)};
//为特定SalesOrderHeader对象创建密钥。
EntityKey=new EntityKey(“EntityContext.Deparment”,
entityKeyValues);
//通过其键从上下文或持久化存储中获取对象。
if(context.TryGetObjectByKey(key,out实体))
{
ApplyPropertyChanges(key.EntitySetName,Model);
SaveChanges();
}
其他的
{
//如果需要,记录消息
//“找不到具有此键的对象。”
}                
}
捕获(EntitySqlException ex)
{
//日志消息
}
}
}       

当它在其他页面上工作时,我也遇到了同样的问题。我不知道这个评论是否有用。。。但我最终发现,在我的objet(相当于您的部门)中,我的“Id”没有出现在表单中


仅仅是在我的表单上添加它(虽然我已经删除了它…)就解决了我的问题。(使用style=“visibility:hidden”,为了不看到他…

改进Steve Willcock的伟大实现,下面是我的建议

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

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

方法

该方法基本上只是使用反射来动态地获取“Id”属性的值,并对其进行设置。这样就省去了与代理之间的所有麻烦

public static void ApplyDetachedPropertyChanges<T>(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);
}

+1为扩展方法。真漂亮!=)如果表使用多列PK,该方法必须如何更改?您需要对扩展方法进行重载,该方法使用KeyValuePair的IEnumerable指定键,然后在EntityKey上调用相应的构造函数重载。为了维护stype的安全性并避免在KeyValuePair中传递字符串,您可以传递一组“key finding”委托以及要放入键中的值。基于这个伟大的答案,我对它进行了一些改进,并利用它制作了我自己的小方法,我也将其作为答案发布。它适用于任何类型的实体对象,并且