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