EF 4: удаление дочернего объекта из коллекции не удаляет его - почему? - PullRequest
32 голосов
/ 31 марта 2010

Я использую Entity Framework 4 и у меня есть родительско-дочерние отношения с установленным «Каскадным удалением». Поэтому я ожидаю, когда я удаляю дочерний элемент из родительского элемента, этот дочерний элемент удаляется при вызове SaveChanges ().

        cuRepository.Attach(_controlUnit);
        foreach (var recipe in recipes) {
            _controlUnit.Recipes.Remove(recipe);
            //repository.DeleteObject(recipe);
        }

Вместо этого я получаю ошибку:

Произошла исключительная ситуация System.InvalidOperationException Сообщение = Операция fail: отношение не может быть изменено, потому что один или несколько свойства внешнего ключа не обнуляются. Когда изменение сделано в отношение, для свойства внешнего ключа установлено нулевое значение. Если внешний ключ не поддерживает нулевые значения, новое отношение должно быть определено, свойство внешнего ключа должно быть назначено другое ненулевое значение, или несвязанный объект должен быть удален.

Когда я явно удаляю детей (см. Закомментированную строку), все в порядке. Чего мне не хватает?

Ответы [ 5 ]

28 голосов
/ 31 марта 2010

Вы не удаляете объект с помощью оператора remove. Вместо этого вы пытаетесь изменить запись и сделать ее сиротой (установив для внешнего ключа значение null). База данных имеет ненулевое ограничение для этого столбца и не позволяет вам делать это.

26 голосов
/ 14 сентября 2010

http://weblogs.asp.net/zeeshanhirani/archive/2010/07/23/removing-entity-from-a-related-collection.aspx точно объясняет, что с вами произошло.


Предположим, у вас есть дизайн класса примерно такой:

sample class design

Entity Framework сгенерирует необходимые столбцы внешнего ключа и добавит к ним ограничения NOT NULL, поскольку все рецепты всегда будут связаны только с одним ControlUnit.

Таким образом, во время выполнения у вас будут объекты, похожие на следующий макет:

object diagram at runtime

Теперь ваш код вступает в игру и удаляет связь между объектами Recipe и их ControlUnit:

objects with deleted relationships

При попытке сохранить в данный момент в базе данных нет идентификатора ControlUnit для вставки в столбец внешнего ключа NOT NULL. Текущее состояние объекта нарушает приведенную выше диаграмму классов и не может быть сохранено в макете базы данных, сгенерированном в предположении, что каждый рецепт связан с одним ControlUnit. Вот почему база данных отказывается сохранять изменения, и вы видите исключение.

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

"Но я установил ON DELETE CASCADE на отношения ..."

Да, но это срабатывает только при удалении объекта, а не при удалении отношения. При установленном ON DELETE CASCADE это должно работать:

controlUnitRepository.DeleteObject(_controlUnit);
// deletes the ControlUnit and all associated Recipe entities

Если вы хотите инициировать удаление сущностей Рецепта при удалении их связи с ControlUnit, ваши отношения должны быть не простой ассоциацией, а скорее композицией:

updated class diagram with composition

EF изначально не поддерживает это, но вы можете эмулировать поведение, используя идентифицирующие отношения. Как только объект находится в идентифицирующем отношении к родительскому объекту, и это отношение удаляется, объект также удаляется. Кажется, это было твоим намерением с самого начала. Для получения дополнительной информации об идентификации отношений см. Реализация идентификации отношений с EF4 , где я реализовал идентификацию отношений с EF4 и связался с большим количеством материалов для чтения.

10 голосов
/ 31 марта 2010

добавить context.DeleteObject(recipe) внутри цикла

6 голосов
/ 20 июня 2012

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

Идентификационные отношения в основном говорят, что если родителя не существует, то у ребенка нет смысла. Это означает, что EF знает, что при удалении отношения безопасно удалить ребенка.

См. Этот вопрос Идентификация отношений и вставка дочерних сущностей вызывает "Невозможно вставить явное значение для столбца идентификаторов в таблице" , а это Возможно ли удалить дочерний элемент из коллекции и устранить проблемы в SaveChanges?

3 голосов
/ 11 июня 2012

Я использую это расширение, чтобы не добавлять метод в DAL просто для удаления объекта (код взят из http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx):

public static void Delete<T>(this EntityCollection<T> collection, T entityToDelete) where T : EntityObject, IEntityWithRelationships
{
    RelationshipManager relationshipManager = entityToDelete.RelationshipManager;

    IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();
    if (relatedEnd == null)
    {
        throw new Exception("No relationships found for the entity to delete. Entity must have at least one relationship.");
    }

    var query = relatedEnd.CreateSourceQuery() as ObjectQuery;
    if (query == null)
    {
        throw new Exception("The entity to delete is detached. Entity must be attached to an ObjectContext.");
    }

    query.Context.DeleteObject(entityToDelete);
    collection.Remove(entityToDelete);
}

Итак, я удаляю сущность типа Order.Products.Delete(prod).

Ограничения для использования расширения:
- сущность должна иметь отношения;
- Объект должен быть присоединен к ObjectContext.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...