Обновление отношений при сохранении изменений объектов EF4 POCO

Entity Framework 4, объекты POCO и ASP.Net MVC2. У меня есть много разных отношений, скажем между BlogPost и тегами. Это означает, что в моем T4 сгенерированном classе POCO BlogPost у меня есть:

public virtual ICollection Tags { // getter and setter with the magic FixupCollection } private ICollection _tags; 

Я запрашиваю BlogPost и связанные tags из экземпляра объекта ObjectContext и отправляю его на другой уровень (вид в приложении MVC). Позже я возвращаю обновленный BlogPost с измененными свойствами и измененными отношениями. Например, у него были tags «A» «B» и «C», а новые tags – «C» и «D». В моем конкретном примере нет новых тегов, и свойства тегов никогда не меняются, поэтому единственное, что нужно сохранить, это измененные отношения. Теперь мне нужно сохранить это в другом ObjectContext. (Обновление: теперь я попытался сделать в том же экземпляре контекста, а также не удалось.)

Проблема: я не могу правильно сохранить отношения. Я попробовал все, что нашел:

  • Controller.UpdateModel и Controller.TryUpdateModel не работают.
  • Получение старого BlogPost из контекста, а затем изменение коллекции не работает. (с помощью разных методов из следующей точки)
  • Возможно, это сработает, но я надеюсь, что это всего лишь обходное решение, а не решение :(.
  • Функция «Закреплено» Attach / Add / ChangeObjectState для BlogPost и / или тегов во всех возможных комбинациях. Не смогли.
  • Это похоже на то, что мне нужно, но оно не работает (я пытался его исправить, но не могу для своей проблемы).
  • Пробовал ChangeState / Add / Attach / … отношения объектов контекста. Не смогли.

«Не работает» означает, что в большинстве случаев я работал над данным «решением» до тех пор, пока не произведет никаких ошибок и сохранит хотя бы свойства BlogPost. Что происходит с отношениями: обычно tags снова добавляются в таблицу тегов с новыми PK, а сохраненный BlogPost ссылается на те, а не на оригинальные. Конечно, у возвращаемых тегов есть PK, а до методов сохранения / обновления я проверяю PK, и они равны тем, которые есть в базе данных, поэтому, вероятно, EF считает, что они являются новыми объектами, а те ПК являются временными.

Проблема, о которой я знаю, и может сделать невозможным найти автоматическое простое решение: когда коллекция объектов POCO изменяется, это должно произойти по вышеупомянутому свойству виртуальной коллекции, потому что тогда трюк FixupCollection обновит обратные ссылки на другом конце отношений «многие-ко-многим». Однако, когда View «возвращает» обновленный объект BlogPost, этого не произошло. Это означает, что, возможно, нет простого решения моей проблемы, но это меня очень огорчило бы, и я бы ненавидел EF4-POCO-MVC триумф 🙁 Также это означало бы, что EF не может сделать это в среде MVC в зависимости от того, что Типы объектов EF4 используются :(. Я думаю, что отслеживание изменений на основе моментальных снимков должно обнаружить, что измененный BlogPost имеет отношения с тегами с существующими ПК.

Кстати: я думаю, что одна и та же проблема происходит с отношениями один-ко-многим (Google и мой коллега так говорят). Я дам ему попробовать дома, но даже если это работает, это не поможет мне в моих шести отношениях «многие ко многим» в моем приложении :(.

Давайте попробуем так:

  • Прикрепите BlogPost к контексту. После присоединения объекта к контексту состояние объекта, всех связанных объектов и всех отношений установлено в Unchanged.
  • Используйте context.ObjectStateManager.ChangeObjectState, чтобы настроить BlogPost для изменения
  • Итерация через коллекцию тегов
  • Используйте context.ObjectStateManager.ChangeRelationshipState, чтобы установить состояние для связи между текущим тегом и BlogPost.
  • Сохранить изменения

Редактировать:

Думаю, один из моих комментариев дал вам ложную надежду, что EF сделает слияние для вас. Я много играл с этой проблемой, и мой вывод говорит, что EF не сделает этого для вас. Думаю, вы тоже нашли мой вопрос в MSDN . На самом деле таких вопросов в Интернете много. Проблема в том, что четко не указано, как справиться с этим сценарием. Так что давайте посмотрим на проблему:

Проблемный фон

EF необходимо отслеживать изменения для объектов, чтобы они знали, какие записи необходимо обновлять, вставлять или удалять. Проблема в том, что ответственность за отслеживание изменений лежит на ObjectContext. ObjectContext способен отслеживать изменения только для прикрепленных объектов. Объекты, созданные за пределами ObjectContext, вообще не отслеживаются.

Описание проблемы

Основываясь на приведенном выше описании, мы можем четко заявить, что EF более подходит для связанных сценариев, где объект всегда привязан к контексту – типичный для приложения WinForm. Веб-приложениям требуется отключенный сценарий, где контекст закрывается после обработки запроса, а содержимое объекта передается как HTTP-ответ клиенту. Следующий HTTP-запрос предоставляет измененное содержимое объекта, которое необходимо воссоздать, привязать к новому контексту и сохранить. Отдых обычно происходит за пределами контекстной области (многоуровневая архитектура с невосприимчивым к сопротивлению).

Решение

Итак, как бороться с таким отключенным сценарием? При использовании classов POCO у нас есть 3 способа справиться с отслеживанием изменений:

  • Снимок – требует того же контекста = бесполезно для отключенного сценария
  • Прокси-серверы динамического отслеживания – требует того же контекста = бесполезно для отключенного сценария
  • Ручная синхронизация.

Ручная синхронизация на одном объекте – легкая задача. Вам просто нужно прикрепить объект и вызвать AddObject для вставки, DeleteObject для удаления или установки состояния в ObjectStateManager в Modified для обновления. Настоящая боль возникает, когда вам приходится иметь дело с графом объектов вместо единой сущности. Эта боль еще хуже, когда вам приходится иметь дело с независимыми ассоциациями (теми, кто не использует свойство Foreign Key) и многими из многих отношений. В этом случае вам нужно вручную синхронизировать каждый объект в графе объектов, а также каждое отношение в графе объектов.

Ручная синхронизация предлагается в качестве решения по документации MSDN: Прикрепление и отсоединение объектов говорит:

Объекты привязаны к контексту объекта в состоянии без изменений. Если вам нужно изменить состояние объекта или отношения, потому что вы знаете, что ваш объект был изменен в отдельном состоянии, используйте один из следующих способов.

Упомянутыми методами являются ChangeObjectState и ChangeRelationshipState объекта ObjectStateManager = ручное отслеживание изменений. Аналогичное предложение содержится в другой документации по MSDN: « Определение и управление отношениями» :

Если вы работаете с отключенными объектами, вы должны вручную управлять синхронизацией.

Кроме того, есть сообщение в блоге, связанное с EF v1, которые критикуют именно это поведение EF.

Причина решения

EF имеет множество «полезных» операций и настроек, таких как Refresh , Load , ApplyCurrentValues , ApplyOriginalValues , MergeOption и т. Д. Но по моему исследованию все эти функции работают только для одного объекта и влияют только на скалярные параметры (= не навигация свойств и отношений). Я предпочитаю не тестировать эти методы со сложными типами, вложенными в сущность.

Другое предлагаемое решение

Вместо реальной функциональности Merge команда EF предоставляет нечто вроде Self Tracking Entities (STE), которые не решают проблему. Прежде всего, STE работает только в том случае, если такой же экземпляр используется для цельной обработки. В веб-приложении это не так, если вы не храните экземпляр в состоянии состояния или сеансе. Из-за этого я очень недоволен использованием EF, и я собираюсь проверить функции NHibernate. Первое наблюдение говорит, что NHibernate, возможно, обладает такой функциональностью .

Вывод

Я приведу это предположение с единственной ссылкой на другой связанный с этим вопрос на форуме MSDN. Проверьте ответ Zeeshan Hirani. Он является автором Рецептов Entity Framework 4.0 . Если он говорит, что автоматическое слияние объектных графов не поддерживается, я верю ему.

Но все-таки есть вероятность, что я полностью ошибаюсь, и в EF существует функция автоматического слияния.

Изменить 2:

Как вы можете видеть, это уже было добавлено в MS Connect как предложение в 2007 году. MS закрыла его как что-то, что нужно сделать в следующей версии, но на самом деле ничего не было сделано для улучшения этого пробела, кроме STE.

У меня есть решение проблемы, описанной выше Ладиславом. Я создал метод расширения для DbContext, который автоматически выполнит команду add / update / delete на основе разности предоставленного графика и сохраненного графика.

В настоящее время с помощью Entity Framework вам необходимо будет вручную обновлять контакты, проверять, новый ли новый контакт и добавить, проверить, обновлены ли и отредактированы, проверить, удалены ли они, и удалить их из базы данных. Как только вы должны сделать это для нескольких разных агрегатов в большой системе, вы начинаете понимать, что должен быть лучший, более общий способ.

Пожалуйста, посмотрите и посмотрите, может ли это помочь http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-fority-framework-code-first-allowing-automated-updates-of-a- граф-оф-обособленных-субъектов /

Вы можете перейти прямо к коду здесь https://github.com/refactorthis/GraphDiff

Я знаю, что опаздывать на ОП, но поскольку это очень распространенная проблема, я опубликовал ее, если она служит кому-то другому. Я обсуждал эту проблему, и я думаю, что получил довольно простое решение, что я делаю:

  1. Сохраните основной объект (например, блоги), установив его состояние в Модифицированное.
  2. Запросить базу данных для обновленного объекта, включая коллекции, которые мне нужно обновить.
  3. Запросить и преобразовать .ToList () объекты, которые я хочу включить в мою коллекцию.
  4. Обновите коллекцию основного объекта в списке, который я получил с шага 3.
  5. Сохранить изменения();

В следующем примере «dataobj» и «_categories» – это параметры, полученные моим controllerом «dataobj» – это мой главный объект, а «_categories» – это IEnumerable, содержащий идентификаторы категорий, выбранных пользователем в представлении.

  db.Entry(dataobj).State = EntityState.Modified; db.SaveChanges(); dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id); var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null; dataobj.Categories = it; db.SaveChanges(); 

Он даже работает для множественных отношений

Команда Entity Framework знает, что это проблема удобства использования и планирует рассмотреть ее после EF6.

Из команды Entity Framework:

Это вопрос удобства использования, о котором мы знаем, и о чем мы уже думали, и планируем больше работать над пост-EF6. Я создал этот рабочий элемент для отслеживания проблемы: http://entityframework.codeplex.com/workitem/864 Рабочий элемент также содержит ссылку на голосовую запись пользователя для этого – я призываю вас голосовать за него, если у вас есть уже не сделано.

Если это повлияет на вас, проголосуйте за эту функцию в

http://entityframework.codeplex.com/workitem/864

Все ответы были полезны, чтобы объяснить проблему, но никто из них не решил эту проблему для меня.

Я обнаружил, что если я не использовал отношения в родительском объекте, а просто добавил и удалил дочерние сущности, все работало нормально.

Извините за VB, но это то, над чем работает проект, в котором я работаю.

Родительский объект «Отчет» имеет отношение «один-много» к «ReportRole» и имеет свойство «ReportRoles». Новые роли передаются посредством строки, разделенной запятыми, из вызова Ajax.

Первая строка удалит все дочерние сущности, и если я использую «report.ReportRoles.Remove (f)» вместо «db.ReportRoles.Remove (f)», я бы получил ошибку.

 report.ReportRoles.ToList.ForEach(Function(f) db.ReportRoles.Remove(f)) Dim newRoles = If(String.IsNullOrEmpty(model.RolesString), New String() {}, model.RolesString.Split(",")) newRoles.ToList.ForEach(Function(f) db.ReportRoles.Add(New ReportRole With {.ReportId = report.Id, .AspNetRoleId = f})) 
  • Как объявить отношения один к одному с использованием Entity Framework 4 Code First (POCO)
  • Code First: Независимые ассоциации против иностранных ассоциаций?
  • Как сопоставить разбиение таблицы в EF Code First?
  • Определение "POCO"
  • Создание classов POCO в разных проектах проекта с помощью модели Entity Framework
  • Можно ли запретить EntityFramework 4 переписывать настроенные свойства?
  • Interesting Posts

    WPF TextBox и поведение прокрутки

    Функция определяется специальными случаями в GHCi

    UPDATE несколько таблиц в MySQL с использованием LEFT JOIN

    Разделить data.frame на основе уровней фактора в новые data.frames

    Удаление дубликатов файлов с помощью командного файла Windows

    Как я могу взять список имен серверов и добавить для каждого ресурса URI и порт?

    Как включить трассировку WCF?

    Android Studio: адаптируемая папка: как разместить изображения для нескольких точек на дюйм?

    Что такое "Flash Flash Cache" и когда он должен использоваться?

    Как сбросить все флажки с помощью jQuery или чистого JS?

    Как использовать различное масштабирование для каждого монитора?

    Загрузите несколько файлов в Struts2 с помощью Dropzone.js

    Есть ли способ удалить панель задач Windows 7?

    измените размер файла sqlite после «DELETE FROM table»

    Как сервер может вызывать асинхронные изменения на HTML-странице, созданной JSF?

    Давайте будем гением компьютера.