Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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 и связался с большим количеством материалов для чтения.

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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