Можно ли удалить ребенка из коллекции и решить проблемы с SaveChanges?
Мы используем Entity Framework Code First с отношениями внешнего ключа. Мы исследуем способы обработки удаления объектов из сущностей ICollection в нашем приложении.
Когда у нас есть сущность с дочерними отношениями, мы можем добавлять объекты непосредственно в их ICollection, используя метод Add. Теперь, когда вы используете удалить, вы получаете ошибку
System.InvalidOperationException произошло. Сообщение = Операция не выполнена: отношение не может быть изменено, поскольку одно или несколько свойств внешнего ключа не могут иметь значение NULL. Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение. Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть назначено другое ненулевое значение или несвязанный объект должен быть удален.
Я понимаю, что это потому, что Удалить в коллекции только удаляет отношения, обнуляя внешний ключ. Мы хотели написать нашу бизнес-логику в нашей сущности и разрешить удаление.
Так что вытащите корневую сущность из своего репозитория, например Order from OrderRepository, затем вызовите какой-то конкретный метод этой сущности, например Order.AddOrderline(Orderline orderline)
Это добавляет OrderLine к заказам virtual ICollection<OrderLine> OrderLines
Однако мы не можем написать код, как Order.CancelOrderline(int orderLineId)
потому что простое удаление из ICollection вызывает ошибку при изменениях сбережений.
Похоже, что в любом случае достичь этого просто манипулируя коллекциями объектов. Очевидно, что мы можем удалить непосредственно из контекста. Однако я хотел бы сделать это частью сущности. Можем ли мы очистить определенные объекты без внешнего ключа в событии SaveChanges Entity Framework? Очевидно, что необходимо указать EF, какие объекты можно удалить, если они имеют нулевой внешний ключ.
В настоящее время мы используем шаблон репозитория, поэтому контроллер не имеет доступа к контексту. Очевидно, я мог бы использовать репозиторий OrderLine или метод удаления OrderLine в репозитории Order. Однако просто интересно, можно ли было написать код на сущности без ссылок на механизм персистентности.
Мысли? Мы идем об этом все неправильно? Позволяют ли другие ORM просто удалить из дочерних коллекций?
2 ответа
Я не знаю, является ли следующее решение для вас, но 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
автоматически созданная личность, если хотите. Важно только то, что ФК 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
из базы данных.
Более подробно здесь в разделе "Соображения по идентификации и неидентификации отношений".
Как вы обнаружите, если вы просто удаляете Entity из коллекции, Entity остается прикрепленным к контексту объекта и выдает ошибку при вызове SaveChanges()
; Я использовал Domain Events, чтобы включить аккуратный способ удаления сущности из контекста объекта через репозиторий.
Я подробно изложил этот подход в своем ответе на этот вопрос.