ASP.NET MVC/EF4/POCO/Репозиторий - как обновить отношения?

У меня есть соотношение 1... * между Обзор и Рекомендациями.

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

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

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

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

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

Я нашел qaru.site/info/45967/..., который выглядит хорошо, но я пытаюсь выяснить, как заставить это работать с POCO/Repository (и ASP.NET MVC - удаленный контекст).

Поскольку я использую POCO, review.Recommendations является ICollection<Recommendation>, поэтому я не могу сделать review.Recommendations.Attach. Я тоже не использую Self-Tracking Entities, поэтому мне нужно вручную работать с отслеживанием графика/изменения, что до сих пор не было проблемой.

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

Обзор:

  • Рекомендации (ICollection<Recommendation>):
    • РекомендацияOne (Recommendation)
    • Рекомендация 2 (Recommendation)

Если im в представлении редактирования, два флажка уже отмечены. Третий (представляющий рекомендациюThree) не отмечен.

Но если я установил этот флажок, указанная выше модель станет следующей:

Обзор:

  • Рекомендации (ICollection<Recommendation>):
    • РекомендацияOne (Recommendation)
    • Рекомендация 2 (Recommendation)
    • РекомендацияТри (Recommendation)

И поэтому мне нужно привязать RecommendationThree к графику как новый объект.

Нужны ли мне скрытые поля для сравнения опубликованных данных с существующим объектом? Или я должен сохранить объект в 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)), в зависимости от того, что это новое/измененное/удаленное.

Ответ 1

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

recommendations.Add(newRecomendation)

?

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

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

SomeServiceOrRepository.AddNewRecommendation( newRecommendation )

или

SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )

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

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

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

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

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

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

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

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

Ответ 2

Я принял ответ @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.

Теперь, конечно, это не решение concurrency -proof.

Если я загружаю View View View и перед тем, как нажать "Отправить", кто-то изменит Обзор, мои изменения могут быть потеряны.

К счастью, у меня есть режим "last-in-wins" concurrency, поэтому для меня это не проблема.

Мне нравится POCO, но человек - это боль, когда у вас есть комбинация безстоящей среды (MVC) и отслеживания изменений.

Ответ 3

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

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

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