EntityKey и ApplyPropertyChanges () - PullRequest
6 голосов
/ 22 мая 2009

Мне нужно установить EntityKey EntityObject. Я знаю его тип и значение 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 не содержат ObjectStateEntry с ссылка на объект типа 'Sample.Models.Department'.

Два EntityKeys равны. Почему второй блок кода не работает? Как я могу это исправить?

Ответы [ 5 ]

9 голосов
/ 22 мая 2009

Причина, по которой ваш второй блок кода дает сбой, заключается в том, что EF не может найти объект в ObjectStateManager - то есть, когда он извлекает объекты из БД, он помещает их в диспетчер состояний, чтобы он мог их отслеживать - это похоже на Карта удостоверений шаблон. Несмотря на наличие 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 с актуальными реальными обновленными значениями.

Вот то же самое, заключенное в методе расширения - это должно работать для всего, что использует один столбец БД для первичного ключа. Единственная интересная часть вызова метода заключается в том, что вам нужно указать ему, как найти ключевое свойство через делегат в качестве второго аргумента метода расширения:

[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:)

4 голосов
/ 26 ноября 2009
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);
    }
}
2 голосов
/ 16 октября 2011

Улучшение замечательной реализации Стива Уилкока, вот мое предложение.

Он использует Reflection (часть .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);
}

Использование

Использовать его тоже довольно просто.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
    db.ApplyDetachedPropertyChanges(Model);
    db.SaveChanges();
}
2 голосов
/ 29 июля 2010

Попробуйте использовать приведенный ниже код и дайте мне знать, работает ли он для вас.

[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
        }
    }
 }       
1 голос
/ 15 апреля 2011

У меня была такая же проблема, когда она работала на других страницах. Я не знаю, будет ли этот комментарий полезным ... Но я наконец-то обнаружил, что в моем объекте (эквиваленте вашего Департамента) мой "Идентификатор" не появлялся в виде ...

Тот факт, что он был добавлен в форму (хотя я его удалил ...), решил мою проблему. (со стилем = "видимость: скрыто", чтобы не видеть его ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...