ASP.NET MVC / EF4 / POCO / Репозиторий - Как обновить отношения?
У меня 1..* отношения между Обзором и Рекомендациями.
Соответствующая часть моей модели (которая также является POCO, отображаемой EF4):
public class Review
{
public ICollection<Recommendations> Recommendations { get; set; }
}
В режиме редактирования я представляю Рекомендации в виде набора флажков.
Когда я пытаюсь добавить новую Рекомендацию как часть редактирования Обзора (например, установите другой флажок), ничего не происходит - и я знаю, почему...
Я использую "метод заглушки" для обновления своих сущностей - например, я создаю сущность с тем же ключом, присоединяю ее к графику, затем делаю ApplyCurrentValues
, Но это работает только для скалярных свойств, а не для навигационных свойств.
Я нашел этот вопрос Stackru, который выглядит хорошо, но я пытаюсь выяснить, как заставить это работать с POCO /Repository (и ASP.NET MVC - отдельный контекст).
Как я использую POCO, review.Recommendations
является ICollection<Recommendation>
так что я не могу сделать review.Recommendations.Attach
, Я также не использую Self-Tracking Entities, поэтому мне нужно вручную работать с графиком / отслеживанием изменений - что до сих пор не было проблемой.
Таким образом, вы можете визуализировать сценарий:
Обзор:
- Рекомендации (
ICollection<Recommendation>
):- Рекомендация одна (
Recommendation
) - Рекомендация два (
Recommendation
)
- Рекомендация одна (
Если я в режиме редактирования, два из флажков уже установлены. Третий (представляющий Рекомендацию Три) не проверен.
Но если я установлю этот флажок, приведенная выше модель станет:
Обзор:
- Рекомендации (
ICollection<Recommendation>
):- Рекомендация одна (
Recommendation
) - Рекомендация два (
Recommendation
) - Рекомендация Три (
Recommendation
)
- Рекомендация одна (
И поэтому мне нужно прикрепить Рекомендацию Три к графу как новую сущность.
Нужны ли скрытые поля для сравнения опубликованных данных существующей сущности? Или я должен сохранить объект в TempData и сравнить его с опубликованным объектом?
РЕДАКТИРОВАТЬ
Чтобы избежать путаницы, вот полный вызов стека приложения:
ReviewController
[HttpPost]
public ActionResult Edit(Review review)
{
_service.Update(review); // UserContentService
_unitOfWork.Commit();
}
UserContentService
public void Update<TPost>(TPost post) where TPost : Post, new()
{
_repository.Update(post); // GenericRepository<Post>
}
GenericRepository - используется какGenericRepository<Post>
public void Update<T2>(T2 entity) where T2 : class, new()
{
// create stub entity based on entity key, attach to graph.
// override scalar values
CurrentContext.ApplyCurrentValues(CurrentEntitySet, entity);
}
Итак Update
(или же Add
или же Delete
) Для каждой рекомендации необходимо вызывать методы репозитория, в зависимости от того, новые они / модифицированные / удаленные.
3 ответа
Возможно, мне нужно больше контекста, но что не так с:
recommendations.Add(newRecomendation)
?
В ответ на комментарий:
Хорошо, так что не так с
SomeServiceOrRepository.AddNewRecommendation( newRecommendation )
или же
SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )
Последнее предложение? Вы имеете в виду два вопроса?
Это не должно быть сложно вообще.
Подводя итог моему ответу, я думаю, что вы делаете вещи "трудным путем" и действительно должны сосредоточиться на публикации значений форм, которые соответствуют действиям CRUD, которые вы пытаетесь выполнить.
Если новая сущность может появиться в то же время, что и ваши отредактированные сущности, вы должны по-настоящему префиксировать их по-другому, чтобы связыватель модели мог уловить их. Даже если у вас есть несколько новых элементов, вы можете использовать один и тот же синтаксис [0], просто добавьте в поле "name" значение New или что-то еще.
Часто в этом сценарии нельзя полагаться на функции графа Entity Frameworks, поскольку удаление объекта из коллекции никогда не означает, что его следует установить для удаления.
Если форма неизменна, вы также можете попробовать использовать обобщенную функцию присоединения от ObjectSet:
theContect.ObjectSet<Review>().Attach( review )
Тонн выходов из этого. Может быть, вы могли бы разместить свой контроллер и просмотреть код?
Я принял ответ @jfar, потому что он поставил меня на правильный путь, но подумал, что добавлю ответ сюда для пользы других людей.
Причина, по которой отношения не обновлялись по следующим причинам:
1) Полностью отключенный сценарий. ASP.NET = без сохранения состояния, новый контекст обновляет каждый HTTP-запрос.
2) Отредактированная сущность, созданная MVC (привязка модели), но не существующая в графе.
3) При использовании POCO без отслеживания изменений, выполнение .Attach
на объект добавит его в граф, но объект и любые дочерние отношения будут неизменными.
4) Я использую трюк с заглушкой и ApplyCurrentValues
обновить сущность, но это работает только для скалярных свойств, а не навигационных.
Итак, чтобы заставить работать вышеперечисленное, мне нужно было бы EntityState
для объекта (который происходит автоматически из-за ApplyCurrentValues
), а также навигационные свойства.
И есть проблема - как я узнаю, что навигационное свойство было добавлено / изменено / удалено? У меня нет объекта для сравнения - только объект, который, как я знаю, был "отредактирован", но я не знаю, что было отредактировано.
Таким образом, решение в конце концов было таким:
[HttpPost]
public ActionResult Edit(Review review)
{
var existingReview = _service.FindById(review.Id); // review is now in graph.
TryUpdateModel(existingReview); // MVC equivalent of "ApplyCurrentValues" - but works for ALL properties - including navigationals
_unitOfWork.Commit(); // save changed
}
Вот и все. Мне даже не нужен мой _service.Update
метод - так как мне больше не нужен трюк с заглушкой - потому что обзор находится в графе с поиском, и ApplyCurrentValues
заменяется TryUpdateModel
,
Теперь, конечно, - это не решение для параллелизма.
Если я загружаю представление редактирования обзора и, прежде чем я нажимаю "Отправить", кто-то другой изменяет обзор, мои изменения могут быть потеряны.
К счастью, у меня есть режим параллелизма "последний в выигрыше", так что это не проблема для меня.
Я люблю POCO, но люди испытывают боль, когда у вас есть сочетание среды без состояния (MVC) и отслеживания изменений.
Работа с независимыми графами объектов - мой любимый недостаток EF. Просто боль в заднице. Сначала вы должны разобраться с этим самостоятельно. EF не поможет вам в этом. Это означает, что в дополнение к Review
Вы также должны отправить некоторую информацию о внесенных изменениях. Когда вы прикрепляете Review
в контекст это устанавливает Review
все Recommendation
и все отношения к Unchanged
государство. ApplyCurrentValues
работает только для скалярных значений, как вы уже нашли. Таким образом, вы должны использовать вашу дополнительную информацию о внесенных изменениях и установить состояние отношений Added
используя ObjectContext.ObjectStateManager.ChangeRelationshipState
,
Я лично отказался от этого подхода, и я загружаю граф объектов из БД, сначала объединяю свои изменения в прикрепленный граф и сохраняю его.
Я ответил на подобный вопрос более глубоко здесь.