Можно ли удалить ребенка из коллекции и решить проблемы в SaveChanges?

Мы используем Entity Framework Code First с отношениями с внешним ключом. Мы изучаем способы обращения с удалением объектов из объектов ICollection в нашем приложении.

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

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

Я понимаю, это потому, что Remove в коллекции удаляет связь только путем обнуления внешнего ключа. Мы хотели написать нашу бизнес-логику в нашей организации и разрешить удаление.

Итак, выведите из него корневой объект Repostiory, например, Order from OrderRepository, затем вызовите некоторый конкретный метод объекта, например. Order.AddOrderline(Orderline orderline) Это добавляет OrderLine к Заказу virtual ICollection<OrderLine> OrderLines

Однако мы не можем писать код типа Order.CancelOrderline(int orderLineId), потому что простое удаление из ICollection вызывает ошибку изменения сбережений.

Похоже, что этого не добиться, просто манипулируя коллекциями объектов. Очевидно, мы можем удалить непосредственно из контекста. Однако я хотел бы сделать его частью объекта. Можем ли мы очистить определенные объекты без внешнего ключа в событии SaveChanges для Entity Framework? Очевидно, необходимо указать EF, какие объекты можно удалить, если они имеют нулевой внешний ключ.

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

Мысли? Мы все это делаем неправильно? Другие ORM позволяют просто удалить из Child Collections?

Ответ 1

Я не знаю, подходит ли вам решение, но Entity Framework поддерживает Идентификация отношений. В такой связи внешний ключ дочернего объекта (зависимый) от родителя (принципала) должен быть частью (составного) первичного ключа дочернего объекта. Например - с аннотациями DbContext - ваши классы моделей должны выглядеть так:

public class Order
{
    [Key]
    public int OrderId { get; set; }

    public ICollection<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    [Key, ForeignKey("Order"), Column(Order = 1)]
    public int OrderId { get; set; }

    [Key, Column(Order = 2)]
    public int OrderLineId { get; set; }

    public Order Order { get; set; }
}

Вы можете сделать OrderLineId автогенерированный идентификатор, если хотите. Важно только то, что FK до Order является частью ПК.

Код, подобный этому, например...

using (var ctx = new MyContext())
{
    var order = ctx.Orders.Include("OrderLines").Single(o => o.OrderId == 1);
    var orderLineToDelete = order.OrderLines
        .FirstOrDefault(ol => ol.OrderLineId == 5);
    if (orderLineToDelete != null)
        order.OrderLines.Remove(orderLineToDelete);

    ctx.SaveChanges();
}

... действительно удалит orderLineToDelete из базы данных.

Подробнее здесь в разделе "Соображения для идентификации и неидентификации отношений".

Ответ 2

Как вы находите, если вы просто удаляете Entity из коллекции, Entity зависает в привязке к контексту объекта и выдает сообщение об ошибке при вызове SaveChanges(); Я использовал события домена, чтобы включить аккуратный способ удаления объекта из контекста объекта через репозиторий.

Я подробно описал этот подход в своем ответе на этот вопрос.