EF 4: удаление дочернего объекта из коллекции не удаляет его - почему?

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

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

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

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

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

5 ответов

Решение

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

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


Предполагая, что у вас есть дизайн класса что-то вроде этого:

образец дизайна класса

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

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

диаграмма объекта во время выполнения

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

объекты с удаленными отношениями

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

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

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

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

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

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

обновлена ​​диаграмма классов с составом

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

Добавлять context.DeleteObject(recipe) внутри петли

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

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

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

Я использую это расширение, чтобы не добавлять метод в 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.

Другие вопросы по тегам