ASP.NET MVC / EF4 / POCO / Репозиторий - Как обновить отношения? - PullRequest
13 голосов
/ 11 января 2011

У меня 1 .. * отношение между Отзывом и Рекомендациями .

Соответствующая часть моей модели (которая также является POCO, отображаемой EF4):

public class Review
{
   public ICollection<Recommendations> Recommendations { get; set; }
}

В представлении Edit я представляю Рекомендации в виде набора флажков.

Когда я пытаюсь добавить новую Рекомендацию как часть редактирования Обзора (например, установите другой флажок), ничего не происходит - и я знаю, почему ...

Я использую «метод заглушки» для обновления своих сущностей - например, я создаю сущность с тем же ключом, прикрепляю ее к графику, затем выполняю ApplyCurrentValues. Но это работает только для скалярных свойств, но не для навигационных свойств.

Я нашел этот вопрос StackOverflow , который выглядит хорошо, но я пытаюсь выяснить, как заставить его работать с 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)

И поэтому мне нужно прикрепить Рекомендацию 3 к графику в виде нового объекта .

Нужны ли скрытые поля для сравнения опубликованных данных с существующим объектом? Или мне следует сохранить объект в TempData и сравнить его с опубликованным объектом?

EDIT

Чтобы избежать путаницы, вот полный вызов стека приложений:

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 ]

8 голосов
/ 12 января 2011

Я принял ответ @ jfar, потому что он поставил меня на правильный путь, но подумал, что я добавлю ответ сюда для пользы других людей.

Причина, по которой отношения не обновлялись по следующим причинам:

1) Полностью отключенный сценарий. ASP.NET = без сохранения состояния, новый контекст обновляет каждый HTTP-запрос.

2) Отредактированная сущность, созданная MVC (привязка модели), но не существующая в графе.

3) При использовании POCO без отслеживания изменений выполнение .Attach для объекта добавит его в график, но объект и любые дочерние отношения будут неизменными.

4) Я использую трюк с сущностью-заглушкой и ApplyCurrentValues, чтобы обновить сущность, но это работает только для скалярных свойств, а не для навигационных.

Итак, чтобы заставить работать вышеперечисленное, мне нужно было бы установить 1011 * для объекта (что происходит автоматически из-за 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) и отслеживания без изменений.

6 голосов
/ 12 января 2011

Работа с графами обособленных объектов - мой любимый недостаток EF.Просто боль в заднице.Сначала вы должны разобраться с этим самостоятельно.EF не поможет вам в этом.Это означает, что в дополнение к Review вам также необходимо отправить некоторую информацию о внесенных изменениях.Когда вы присоединяете Review к контексту, он устанавливает Review все Recommendation и все отношения в Unchanged состояние.ApplyCurrentValues работает только для скалярных значений, как вы уже нашли.Таким образом, вы должны использовать вашу дополнительную информацию о внесенных изменениях и установить состояние отношений на Added, используя ObjectContext.ObjectStateManager.ChangeRelationshipState.

Я лично отказался от этого подхода, и я загружаю граф объектов из БД при первом объединениимои изменения в прикрепленный график и сохранить его.

Я ответил на аналогичный вопрос более подробно здесь .

4 голосов
/ 11 января 2011

Возможно, мне нужно больше контекста, но что не так с:

recommendations.Add(newRecomendation)

В ответ на комментарий:

Хорошо, что не так с

SomeServiceOrRepository.AddNewRecommendation( newRecommendation )

или

SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )

Последнее предложение? Вы имеете в виду два вопроса?

Это не должно быть сложно.

Подводя итог моему ответу, я думаю, что вы делаете вещи "сложным путем" и действительно должны сосредоточиться на публикации значений форм, которые соответствуют действиям CRUD, которые вы пытаетесь выполнить.

Если новая сущность может появиться в то же время, что и ваши отредактированные сущности, вы должны действительно префиксировать их по-разному, чтобы связыватель модели мог уловить ее. Даже если у вас есть несколько новых элементов, вы можете использовать один и тот же синтаксис [0], просто добавьте в поле «name» значение New или что-то в этом роде.

Часто в этом сценарии нельзя полагаться на функции графика Entity Frameworks, поскольку удаление объекта из коллекции никогда не означает, что его следует установить для удаления.

Если форма неизменна, вы также можете попробовать использовать обобщенную функцию присоединения из ObjectSet:

theContect.ObjectSet<Review>().Attach( review )

Тонны выходов из этого. Может быть, вы могли бы опубликовать свой контроллер и просмотреть код?

...