Я получаю эту ошибку, когда я получаю GetById () для сущности, а затем устанавливаю коллекцию дочерних сущностей в свой новый список, который поступает из представления MVC.
Операция не выполнена: отношение не может быть изменено, так как одно или несколько свойств внешнего ключа не могут иметь значение NULL. Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть назначено другое ненулевое значение или несвязанный объект должен быть удален.
Я не совсем понимаю эту строку:
Невозможно изменить отношение, так как одно или несколько свойств внешнего ключа не могут иметь значение NULL.
Зачем мне менять отношения между двумя сущностями? Он должен оставаться неизменным на протяжении всего срока службы всего приложения.
Код, в котором возникает исключение, - это простое назначение измененных дочерних классов в коллекции существующему родительскому классу. Мы надеемся, что это поможет удалить дочерние классы, добавить новые и модификации. Я бы подумал, что Entity Framework справится с этим.
Строки кода можно перевести на:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
Ответы:
Вы должны удалить старые дочерние элементы
thisParent.ChildItems
по одному вручную. Entity Framework не делает это для вас. Наконец, он не может решить, что вы хотите сделать со старыми дочерними элементами - хотите ли вы выбросить их или хотите сохранить и назначить их другим родительским объектам. Вы должны сообщить Entity Framework о своем решении. Но одно из этих двух решений вы ДОЛЖНЫ принять, поскольку дочерние объекты не могут жить в одиночестве без ссылки на какого-либо родителя в базе данных (из-за ограничения внешнего ключа). Это в основном то, что говорит исключение.редактировать
Что бы я сделал, если бы дочерние элементы могли быть добавлены, обновлены и удалены:
Примечание: это не проверено. Предполагается, что коллекция дочерних элементов имеет тип
ICollection
. (У меня обычноIList
и затем код выглядит немного иначе.) Я также убрал все абстракции репозитория, чтобы сделать его простым.Я не знаю, является ли это хорошим решением, но я считаю, что необходимо проделать некоторую тяжелую работу в этом направлении, чтобы позаботиться обо всех видах изменений в навигационной коллекции. Я также был бы рад увидеть более простой способ сделать это.
источник
Причина, по которой вы сталкиваетесь с этим, заключается в разнице между составом и агрегацией .
В композиции дочерний объект создается при создании родителя и уничтожается при уничтожении его родителя . Таким образом, его время жизни контролируется его родителем. например, сообщение в блоге и его комментарии. Если сообщение удалено, его комментарии должны быть удалены. Нет смысла оставлять комментарии к несуществующему сообщению. То же самое для заказов и элементов заказа.
В совокупности дочерний объект может существовать независимо от его родителя . Если родитель уничтожен, дочерний объект все еще может существовать, так как он может быть добавлен к другому родителю позже. Например: связь между списком воспроизведения и песнями в этом списке воспроизведения. Если список воспроизведения удален, песни не должны быть удалены. Они могут быть добавлены в другой список воспроизведения.
Entity Framework различает агрегацию и составные отношения следующим образом:
Для композиции: ожидается, что дочерний объект будет иметь составной первичный ключ (ParentID, ChildID). Это сделано специально, так как идентификаторы детей должны быть в пределах их родителей.
Для агрегации: он ожидает, что свойство внешнего ключа в дочернем объекте будет обнуляемым.
Итак, причина, по которой вы столкнулись с этой проблемой, заключается в том, как вы установили свой первичный ключ в своей дочерней таблице. Он должен быть составным, но это не так. Итак, Entity Framework рассматривает эту ассоциацию как агрегацию, что означает, что когда вы удаляете или очищаете дочерние объекты, она не собирается удалять дочерние записи. Он просто удалит связь и установит для соответствующего столбца внешнего ключа значение NULL (чтобы эти дочерние записи впоследствии могли быть связаны с другим родителем). Поскольку ваш столбец не допускает NULL, вы получите исключение, которое вы упомянули.
Решения:
1. Если у вас есть веская причина не использовать составной ключ, вам необходимо явно удалить дочерние объекты. И это можно сделать проще, чем решения, предложенные ранее:
2- В противном случае, установив правильный первичный ключ на вашей дочерней таблице, ваш код будет выглядеть более значимым:
источник
Это очень большая проблема. Что на самом деле происходит в вашем коде:
Parent
из базы данных и получаете прикрепленный объектТеперь решение действительно зависит от того, что вы хотите сделать и как бы вы хотели это сделать?
Если вы используете ASP.NET MVC, вы можете попробовать использовать UpdateModel или TryUpdateModel .
Если вы хотите просто обновить существующих потомков вручную, вы можете просто сделать что-то вроде:
Присоединение на самом деле не нужно (установка состояния
Modified
также присоединит сущность), но мне это нравится, потому что это делает процесс более очевидным.Если вы хотите изменить существующий, удалить существующий и вставить новые дочерние элементы, вы должны сделать что-то вроде:
источник
.Clone()
. Имеете ли вы в виду, что aChildItem
имеет другие дочерние свойства навигации? Но в таком случае, не хотим ли мы, чтобы весь подграф был привязан к контексту, поскольку мы ожидаем, что все дочерние элементы являются новыми объектами, если сам дочерний элемент является новым? (Ну, это может отличаться от модели к модели, но давайте предположим, что дочерние элементы «зависят» от ребенка, как дочерние элементы зависят от родителя.)http://stackoverflow.com/questions/20233994/do-i-need-to-create-a-dbset-for-every-table-so-that-i-can-persist-child-entitie
Я нашел этот ответ гораздо более полезным для той же ошибки. Кажется, что EF не нравится, когда вы удаляете, он предпочитает удалить.
Вы можете удалить коллекцию записей, прикрепленных к такой записи.
В этом примере для всех подробных записей, прикрепленных к ордеру, установлено состояние «Удалить». (При подготовке к добавлению обратно обновленных сведений в рамках обновления заказа)
источник
Я понятия не имею, почему два других ответа так популярны!
Я полагаю, что вы были правы, предполагая, что среда ORM должна справиться с этим - в конце концов, это то, что она обещает предоставить. В противном случае модель вашего домена будет повреждена постоянными проблемами. NHibernate справится с этим, если вы правильно настроите параметры каскада. В Entity Framework это также возможно, они просто ожидают, что вы будете следовать лучшим стандартам при настройке модели вашей базы данных, особенно когда им нужно определить, какое каскадирование следует выполнить:
Вы должны правильно определить родительско-дочерние отношения , используя « идентифицирующие отношения ».
Если вы сделаете это, Entity Framework знает, что дочерний объект идентифицирован родителем, и, следовательно, это должна быть ситуация «каскадного удаления-сироты».
Помимо вышесказанного, вам может понадобиться (из опыта NHibernate)
вместо полной замены списка.
ОБНОВИТЬ
Комментарий @ Slauma напомнил мне, что отдельные объекты являются еще одной частью общей проблемы. Чтобы решить эту проблему, вы можете использовать подход связывания пользовательских моделей, который создает ваши модели, пытаясь загрузить его из контекста. Этот пост блога показывает пример того, что я имею в виду.
источник
parent.ChildItems.Remove
вместо_dbContext.ChildItems.Remove
. По-прежнему (EF <= 6) нет встроенной поддержки EF, чтобы избежать длинного кода, подобного тому, который содержится в других ответах.return context.Items.Find(id) ?? new Item()
Если вы используете AutoMapper с Entity Framework в том же классе, вы можете столкнуться с этой проблемой. Например, если ваш класс
Это попытается скопировать оба свойства. В этом случае ClassBId не обнуляется. Поскольку AutoMapper будет копировать,
destination.ClassB = input.ClassB;
это вызовет проблему.Установите свой AutoMapper, чтобы игнорировать
ClassB
свойство.источник
У меня просто была такая же ошибка. У меня есть две таблицы с родительскими и дочерними отношениями, но я настроил «каскад удаления» для столбца внешнего ключа в определении таблицы дочерней таблицы. Поэтому, когда я вручную удаляю родительскую строку (через SQL) в базе данных, она автоматически удаляет дочерние строки.
Однако это не сработало в EF, обнаружилась ошибка, описанная в этой теме. Причиной этого было то, что в моей модели данных сущностей (файл edmx) свойства ассоциации между родительской и дочерней таблицами были неправильными.
End1 OnDelete
Вариант был настроен бытьnone
( «END1» в моей модели конец которой имеет множественность 1).Я вручную изменил
End1 OnDelete
опцию наCascade
и чем это сработало. Я не знаю, почему EF не может поднять это, когда я обновляю модель из базы данных (у меня есть база данных первой модели).Для полноты вот так выглядит мой код для удаления:
Если бы я не определил каскадное удаление, мне пришлось бы удалить дочерние строки вручную перед удалением родительской строки.
источник
Это происходит потому, что дочерний объект помечен как измененный, а не как удаленный.
И модификация, которую EF выполняет с дочерним объектом при
parent.Remove(child)
выполнении, просто устанавливает ссылку на его родителяnull
.Вы можете проверить EntityState дочернего элемента, введя следующий код в Immediate Window Visual Studio, когда возникает исключение после выполнения
SaveChanges()
:где X следует заменить удаленным объектом.
Если у вас нет доступа к
ObjectContext
исполнению_context.ChildEntity.Remove(child)
, вы можете решить эту проблему, сделав внешний ключ частью первичного ключа дочерней таблицы.Таким образом, если вы выполните
parent.Remove(child)
, EF правильно пометит сущность как удаленную.источник
Этот тип решения помог мне:
Важно сказать, что это удаляет все записи и вставляет их снова. Но для моего случая (менее 10) это нормально.
Я надеюсь, что это помогает.
источник
Я столкнулся с этой проблемой сегодня и хотел поделиться своим решением. В моем случае решение заключалось в удалении дочерних элементов перед получением родительского элемента из базы данных.
Ранее я делал это, как в коде ниже. Я получу ту же ошибку, указанную в этом вопросе.
Что сработало для меня, так это сначала получить дочерние элементы, используя parentId (внешний ключ), а затем удалить эти элементы. Затем я могу получить Parent из базы данных, и в этот момент у него больше не должно быть дочерних элементов, и я могу добавлять новые дочерние элементы.
источник
Вы должны вручную очистить коллекцию ChildItems и добавить в нее новые элементы:
После этого вы можете вызвать метод расширения DeleteOrphans, который будет обрабатывать осиротевшие объекты (он должен вызываться между методами DetectChanges и SaveChanges).
источник
context.DetectChanges();
.Я пробовал эти и многие другие решения, но ни одно из них не сработало. Так как это первый ответ на Google, я добавлю свое решение здесь.
Метод, который хорошо работал для меня, заключался в том, чтобы исключать отношения во время коммитов, поэтому EF ничего не испортил. Я сделал это, заново найдя родительский объект в DBContext и удалив его. Поскольку свойства навигации вновь найденного объекта равны нулю, дочерние отношения игнорируются при фиксации.
Обратите внимание, что предполагается, что внешние ключи настроены с помощью ON DELETE CASCADE, поэтому при удалении родительской строки дочерние элементы будут очищены базой данных.
источник
Я использовал решение Моша , но для меня не было очевидным, как правильно реализовать ключ композиции в коде.
Итак, вот решение:
источник
У меня была такая же проблема, но я знал, что она работала нормально в других случаях, поэтому я свел проблему к следующему:
Все, что мне нужно было сделать, это сделать ParentId частью составного PK, чтобы указать, что дети не могут существовать без родителя. Я использовал модель DB-first, добавил PK и пометил столбец parentId как EntityKey (поэтому мне пришлось обновить его как в DB, так и в EF - не уверен, что одного EF будет достаточно).
Когда вы думаете об этом, это очень элегантное различие, которое EF использует, чтобы решить, будут ли дети «иметь смысл» без родителя (в этом случае Clear () не удалит их и не сгенерирует исключение, если вы не установите ParentId на что-то другое / специальное) ), или - как в первоначальном вопросе - мы ожидаем, что элементы будут удалены после удаления из родительского элемента.
источник
Эта проблема возникает из-за того, что мы пытаемся удалить родительскую таблицу, но данные дочерней таблицы присутствуют. Мы решаем проблему с помощью каскадного удаления.
В модели Create метод в классе dbcontext.
После этого в нашем API Call
Каскадное удалениеОпция удаляет родительскую, а также родительскую дочернюю таблицу с помощью этого простого кода. Попробуй так просто.
Удалить диапазон, который используется для удаления списка записей в базе данных Спасибо
источник
Я также решил свою проблему с ответом Моша, и я подумал, что ответ PeterB был немного, так как он использовал перечисление в качестве внешнего ключа. Помните, что вам нужно будет добавить новую миграцию после добавления этого кода.
Я также могу порекомендовать этот блог для других решений:
http://www.kianryan.co.uk/2013/03/orphaned-child/
Код:
источник
Используя решение Slauma, я создал несколько общих функций, которые помогают обновлять дочерние объекты и коллекции дочерних объектов.
Все мои постоянные объекты реализуют этот интерфейс
С этим я реализовал эти две функции в моем репозитории
Чтобы использовать это, я делаю следующее:
Надеюсь это поможет
ДОПОЛНИТЕЛЬНО: Вы также можете создать отдельный класс DbContextExtentions (или ваш собственный контекстный интерфейс):
и используйте это как:
источник
Я столкнулся с той же проблемой, когда собирался удалить свою запись, чем возникла какая-то проблема, потому что решение этой проблемы состоит в том, что когда вы собираетесь удалить свою запись, чем вы что-то упустили перед удалением заголовка / основной записи, вы должны написать в код для удалите его детали перед заголовком / Master Я надеюсь, что ваш вопрос будет решен.
источник
Если вы используете Auto mapper и сталкиваетесь с проблемой, следующим является хорошее решение, оно работает для меня
https://www.codeproject.com/Articles/576393/Solutionplusto-aplus-Theplusoperationplusfailed
Поскольку проблема в том, что мы отображаем нулевые навигационные свойства, и нам фактически не нужно, чтобы они обновлялись в сущности, поскольку они не изменились в контракте, мы должны игнорировать их в определении сопоставления:
Итак, мой код закончился так:
источник
Я столкнулся с этой проблемой раньше, чем через несколько часов, и попробовал все, но в моем случае решение отличалось от перечисленного выше.
Если вы используете уже извлеченную сущность из базы данных и пытаетесь изменить ее дочерние элементы, произойдет ошибка, но если вы получите свежую копию сущности из базы данных, проблем быть не должно. Не используйте это:
Использовать это:
источник