C# EntityKey 和 ApplyPropertyChanges()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/898260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 22:05:15  来源:igfitidea点击:

EntityKey and ApplyPropertyChanges()

c#.netasp.net-mvcentity-framework

提问by Zack Peterson

I need to set an EntityObject's EntityKey. I know its type and its id value. I don't want to query the database unnecessarily.

我需要设置一个EntityObject 的EntityKey。我知道它的类型和它的 id 值。我不想不必要地查询数据库。

This works...

这工作...

//
// 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");
}

This fails...

这失败了...

//
// 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");
}

The ApplyPropertyChanges()line fails with this exception:

ApplyPropertyChanges()线路出现故障与此异常:

The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'Sample.Models.Department'.

ObjectStateManager 不包含带有对“Sample.Models.Department”类型对象的引用的 ObjectStateEntry。

The two EntityKeys are equal. Why does the second block of code fail? How can I fix it?

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

回答by Steve Willcock

The reason your second block of code fails is because EF can't find the object in the ObjectStateManager - i.e. when it pulls objects from the db it puts them in the state manager so it can track them - this is similar to the Identity Mappattern. Despite having an EntityKey, your object isn't in the state manager so EF is unable to persist the changes. You can get around this by putting the object into the state manager youself but you have be a bit sneaky about it.

您的第二个代码块失败的原因是因为 EF 在 ObjectStateManager 中找不到对象 - 即当它从数据库中提取对象时,它会将它们放在状态管理器中以便它可以跟踪它们 - 这类似于身份映射模式. 尽管有 EntityKey,但您的对象不在状态管理器中,因此 EF 无法保留更改。您可以通过自己将对象放入状态管理器来解决这个问题,但您对此有点偷偷摸摸。

This works:

这有效:

[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();
}

... but it's not very clean. Basically this is attaching an 'empty' object with just an entity key, accepting all changes and then calling ApplyPropertyChanges with the actual real updated values.

......但它不是很干净。基本上,这是附加一个只有实体键的“空”对象,接受所有更改,然后使用实际的真实更新值调用 ApplyPropertyChanges。

Here's the same thing wrapped up in an extension method - this should work for anything that has uses a single db column for the primary key. The only interesting part of calling the method is that you need to tell it how to find the key property via a delegate as the second argument to the extension method:

这是包含在扩展方法中的相同内容 - 这应该适用于使用单个 db 列作为主键的任何内容。调用该方法唯一有趣的部分是您需要告诉它如何通过委托作为扩展方法的第二个参数来查找关键属性:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department model)
{
  db.ApplyDetachedPropertyChanges(model, x => x.Id);
  db.SaveChanges();
}

and the extension method:

和扩展方法:

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);
  }
}

As the extension method is calling AcceptAllChanges, you'd need to be careful about calling this if you are doing updates on multiple entities at once - you could easily 'lose' updates if you aren't careful. Hence this approach is only really suitable for simple update scenarios - e.g. a lot of MVC action methods :)

由于扩展方法正在调用 AcceptAllChanges,如果您一次对多个实体进行更新,则需要小心调用它 - 如果不小心,很容易“丢失”更新。因此这种方法只适用于简单的更新场景——例如很多 MVC 操作方法:)

回答by Steve Willcock

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;

        T newEntity = Activator.CreateInstance<T>();
        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);
    }
}

回答by Venkat

Try to use below code and let me know if it is working for you.

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

[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
        }
    }
 }       

回答by PetersLast

I had the same problem, while it was working on other pages. I dont know if this comment will be useful... But I finally discovered, that in my objet (the equivalent of your Departement), my "Id" was not appearing in the form...

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

Just the fact to add it (while I had removed it...) on my form has resolved my problem. (with the style="visibility:hidden", in order not to see him...)

在我的表单上添加它(虽然我已经删除了它......)已经解决了我的问题。(加上style="visibility:hidden",为了看不到他……)

回答by Mathias Lykkegaard Lorenzen

Improving upon Steve Willcock's great implementation, here's my suggestion.

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

It uses Reflection (a part of .NET) way more than the original example, to save you some code. It also automatically supports any type of entity class, and not just a "Department".

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

Furthermore, it gets rid of the obsolete ApplyPropertyChangesmethod, and uses the new ApplyCurrentValuesmethod.

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

The method

方法

The method basically just uses reflection to get the value of the "Id" property dynamically, and setting it too. This saves all the hassle with a delegate.

该方法基本上只是使用反射来动态获取“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);
}

Usage

用法

Using it is pretty simple as well.

使用它也非常简单。

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