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

StackOverflow https://stackoverflow.com/questions/2554696

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

Другие советы

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


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

sample class design

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

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

object diagram at runtime

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

objects with deleted relationships

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

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

"Но я установил 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 и ссылался на дополнительные материалы для чтения.

Добавить 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top