EntityKey и ApplyPropertyChanges ()

.net asp.net-mvc c# entity-framework

Вопрос

Мне нужно установить EntityObject 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 равны. Почему второй блок кода не работает? Как я могу это исправить?

Популярные ответы

Причина, по которой ваш второй блок кода дает сбой, заключается в том, что 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 с фактическими реальными обновленными значениями.

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

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



Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему