Entity Framework 4, объекты POCO и ASP.Net MVC2. У меня есть отношения «многие ко многим», скажем, между объектами BlogPost и Tag. Это означает, что в моем сгенерированном T4 классе POCO BlogPost у меня есть:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
Я прошу BlogPost и связанные теги из экземпляра ObjectContext и отправляю его на другой уровень (просмотр в приложении MVC). Позже я возвращаю обновленный BlogPost с измененными свойствами и измененными отношениями. Например, у него были теги «A», «B» и «C», а новые теги - «C» и «D». В моем конкретном примере нет новых тегов, и свойства тегов никогда не меняются, поэтому единственное, что следует сохранить, - это измененные отношения. Теперь мне нужно сохранить это в другом ObjectContext. (Обновление: теперь я пытался сделать в том же экземпляре контекста, но тоже потерпел неудачу.)
Проблема: я не могу заставить его правильно сохранять отношения. Я перепробовал все, что нашел:
- Controller.UpdateModel и Controller.TryUpdateModel не работают.
- Получение старого BlogPost из контекста и последующее изменение коллекции не работают. (разными методами из следующего пункта)
- Это, вероятно, сработает, но я надеюсь, что это просто обходной путь, а не решение :(.
- Пробовал функции Attach / Add / ChangeObjectState для BlogPost и / или тегов во всех возможных комбинациях. Не смогли.
- Это похоже на то, что мне нужно, но это не работает (я пытался исправить это, но не могу решить свою проблему).
- Пробовал ChangeState / Add / Attach / ... отношения объектов контекста. Не смогли.
«Не работает» означает в большинстве случаев, что я работал над данным «решением» до тех пор, пока оно не выдало ошибок и не сохранило хотя бы свойства BlogPost. Что происходит с отношениями, различается: обычно теги снова добавляются в таблицу тегов с новыми PK, и сохраненный BlogPost ссылается на них, а не на исходные. Конечно, у возвращаемых тегов есть PK, и перед методами сохранения / обновления я проверяю PK, и они равны тем, что есть в базе данных, поэтому, вероятно, EF думает, что это новые объекты, а эти PK являются временными.
Проблема, о которой я знаю, и может сделать невозможным найти автоматическое простое решение: при изменении коллекции объекта POCO это должно произойти с помощью вышеупомянутого свойства виртуальной коллекции, потому что тогда трюк FixupCollection обновит обратные ссылки на другом конце отношения "многие ко многим". Однако, когда View «возвращает» обновленный объект BlogPost, этого не произошло. Это означает, что, возможно, у моей проблемы нет простого решения, но это меня очень огорчит, и я бы возненавидел триумф EF4-POCO-MVC :(. Также это будет означать, что EF не может сделать это в среде MVC, в зависимости от того, что Используются типы объектов EF4 :(. Я думаю, что отслеживание изменений на основе снимков должно определить, что измененный BlogPost имеет отношения к тегам с существующими PK.
Кстати: я думаю, что такая же проблема возникает с отношениями один-ко-многим (так говорят Google и мой коллега). Я попробую дома, но даже если это сработает, это не поможет мне в моих шести отношениях «многие ко многим» в моем приложении :(.
источник
Ответы:
Попробуем так:
Редактировать:
Думаю, один из моих комментариев дал вам ложную надежду на то, что EF выполнит слияние за вас. Я много играл с этой проблемой и пришел к выводу, что EF не сделает этого за вас. Думаю, вы тоже нашли мой вопрос в MSDN . На самом деле таких вопросов в Интернете предостаточно. Проблема в том, что неясно, как бороться с этим сценарием. Итак, давайте посмотрим на проблему:
Фон проблемы
EF необходимо отслеживать изменения в сущностях, чтобы постоянство знало, какие записи необходимо обновить, вставить или удалить. Проблема в том, что отслеживание изменений - это ответственность ObjectContext. ObjectContext может отслеживать изменения только для прикрепленных сущностей. Сущности, созданные вне ObjectContext, вообще не отслеживаются.
Описание проблемы
Основываясь на приведенном выше описании, мы можем четко заявить, что EF больше подходит для подключенных сценариев, где сущность всегда привязана к контексту - типично для приложения WinForm. Веб-приложениям требуется отключенный сценарий, в котором контекст закрывается после обработки запроса, а содержимое объекта передается как HTTP-ответ клиенту. Следующий HTTP-запрос предоставляет измененное содержимое объекта, которое необходимо воссоздать, присоединить к новому контексту и сохранить. Воссоздание обычно происходит вне контекста (многоуровневая архитектура с игнорированием постоянства).
Решение
Так что же делать с таким отключенным сценарием? При использовании классов POCO у нас есть 3 способа справиться с отслеживанием изменений:
Ручная синхронизация одного объекта - простая задача. Вам просто нужно присоединить объект и вызвать AddObject для вставки, DeleteObject для удаления или установить состояние в ObjectStateManager на Modified для обновления. Настоящая боль возникает, когда вам приходится иметь дело с графом объектов, а не с одним объектом. Эта боль еще хуже, когда вам приходится иметь дело с независимыми ассоциациями (теми, которые не используют свойство внешнего ключа) и отношениями многие-ко-многим. В этом случае вам необходимо вручную синхронизировать каждую сущность в графе объектов, а также каждое отношение в графе объектов.
Ручная синхронизация предлагается в качестве решения в документации MSDN: Прикрепление и отсоединение объектов говорит:
Упомянутые методы: ChangeObjectState и ChangeRelationshipState of ObjectStateManager = отслеживание изменений вручную. Аналогичное предложение содержится в другой статье документации MSDN: Определение и управление отношениями гласит:
Более того, есть сообщение в блоге, связанное с EF v1, в котором критикуется именно такое поведение EF.
Причина решения
EF имеет множество «полезных» операций и настроек, таких как Refresh , Load , ApplyCurrentValues , ApplyOriginalValues , MergeOption и т. Д. Но, согласно моему исследованию, все эти функции работают только для одного объекта и влияют только на скалярные свойства (= не свойства и отношения навигации). Я предпочитаю не тестировать эти методы со сложными типами, вложенными в объект.
Другое предлагаемое решение
Вместо реальной функциональности слияния команда EF предлагает нечто под названием Self Tracking Entities (STE), которое не решает проблему. Прежде всего, STE работает, только если один и тот же экземпляр используется для всей обработки. В веб-приложении это не так, если вы не храните экземпляр в состоянии просмотра или сеансе. Из-за этого я очень недоволен использованием EF и собираюсь проверить возможности NHibernate. Первое наблюдение говорит о том, что NHibernate, возможно, имеет такую функциональность .
Вывод
Я закончу это предположение единственной ссылкой на другой связанный вопрос на форуме MSDN. Проверьте ответ Зишана Хирани. Он является автором рецептов Entity Framework 4.0 . Если он говорит, что автоматическое объединение графов объектов не поддерживается, я ему верю.
Но все же есть вероятность, что я полностью ошибаюсь и в EF есть функция автоматического слияния.
Изменить 2:
Как вы можете видеть, это уже было добавлено в MS Connect в качестве предложения в 2007 году. MS закрыла его как нечто, что нужно сделать в следующей версии, но на самом деле ничего не было сделано для исправления этого пробела, кроме STE.
источник
У меня есть решение проблемы, которое описал выше Ладислав. Я создал метод расширения для DbContext, который будет автоматически выполнять добавление / обновление / удаление на основе различий предоставленного графа и постоянного графа.
Пожалуйста, посмотрите, может ли это помочь http://refactorthis.wordpress.com/2012/12/11/introduction-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a- граф-отдельных-сущностей /
Вы можете сразу перейти к коду здесь https://github.com/refactorthis/GraphDiff
источник
Я знаю, что уже поздно для OP, но, поскольку это очень распространенная проблема, я опубликовал это на случай, если он обслужит кого-то еще. Я играл с этой проблемой, и я думаю, что получил довольно простое решение, вот что я делаю:
В следующем примере «dataobj» и «_categories» - это параметры, полученные моим контроллером, «dataobj» - мой основной объект, а «_categories» - это IEnumerable, содержащий идентификаторы категорий, выбранных пользователем в представлении.
Это работает даже для нескольких отношений
источник
Команда Entity Framework осознает, что это проблема удобства использования, и планирует решить ее после выхода EF6.
От команды Entity Framework:
Если это вас коснется, проголосуйте за эту функцию на
http://entityframework.codeplex.com/workitem/864
источник
Все ответы были прекрасны для объяснения проблемы, но ни один из них не решил проблему для меня.
Я обнаружил, что если я не использовал отношения в родительском объекте, а просто добавлял и удалял дочерние объекты, все работало нормально.
Извините за VB, но это то, на чем написан проект, над которым я работаю.
Родительская сущность «Report» имеет отношение «один ко многим» с «ReportRole» и имеет свойство «ReportRoles». Новые роли передаются в виде строки, разделенной запятыми, из вызова Ajax.
Первая строка удалит все дочерние объекты, и если бы я использовал «report.ReportRoles.Remove (f)» вместо «db.ReportRoles.Remove (f)», я бы получил сообщение об ошибке.
источник