Сначала код EF: как удалить строку из коллекции сущности, следуя DDD?
Итак, вот сценарий:
DDD заявляет, что вы используете хранилище для получения совокупного корня, а затем используете его для добавления / удаления в любые имеющиеся у него коллекции.
Добавление просто, вы просто позвоните .Add(Item item)
на Collection
Вы хотите добавить в. Новая строка добавляется в базу данных при сохранении. Тем не менее, удаление отличается - вызов .Remove(Item item)
не удаляет элемент из базы данных, он просто удаляет внешний ключ. Так что, хотя, да, технически он больше не является частью коллекции, он все еще находится в базе данных.
Читая вокруг, единственное решение - удалить его, используя контекст данных. Но в соответствии с DDD объект домена не должен знать о контексте данных, поэтому удаление должно выполняться вне домена.
Как правильно это сделать? Или приемлемо оставить базу данных, заполненную сиротами (возможно, запустив процедуру для их очистки)?
5 ответов
Я решил эту проблему в приложении, над которым я сейчас работаю, используя доменные события; концепция DDD, которую сказал Эрик Эванс, должна была быть в его книге.
В то время как объектам домена не разрешено знать о контексте объекта, IDomainEventHandler
это - поэтому я получил DomainObjectDeletionHandler
который удаляет "удаленные" объекты из контекста объекта, прежде чем элемент управления вернется на уровень приложения и изменения будут сохранены.
Для получения дополнительной информации я написал блог о своей реализации событий в домене и о том, как я подобрал все вместе.
Надеюсь, это поможет:)
редактировать
Например, если у вас есть Order
класс, который имеет OrderItems
коллекция типа OrderItem
:
public class Order
{
// Other stuff
public void RemoveOrderItem(int orderItemId)
{
var orderItemToRemove = OrderItems.First(oi => oi.Id == orderItemId)
OrderItems.Remove(orderItemToRemove);
DomainEvents.Raise(new OrderItemRemoved(orderItemToRemove));
}
}
При удалении дочерней сущности из коллекции EF оставляет ее как потерянную, удаляя только внешний ключ.
Если вы не хотите явно удалять его с помощью DbContext, вы можете использовать то, что называется "Идентификация отношений" ( http://msdn.microsoft.com/en-us/library/ee373856.aspx внизу).
Хитрость заключается в том, чтобы установить составной первичный ключ на дочернем элементе, включая первичный ключ родительского элемента.
После этого при удалении сущности из родительской коллекции она также будет удалена из таблицы.
Я не знаю, является ли это разработанным, но если у подробного объекта есть составной ключ, содержащий ключевые столбцы его главного объекта, он будет автоматически удален, если вы удалите его из коллекции главного объекта. Если у вас есть объект Order с ключом OrderID и навигационным свойством ICollection OrderLines, присвойте OrderLine составной ключ, содержащий OrderID и OrderLineID.
Но так как я не знаю, могу ли я положиться на это, то решение, которое я использовал сам, состоит в том, чтобы позволить EF обрабатывать его так, как он это делает, и фиксировать "отсоединенные" (не в терминах EF) подробные объекты при вызове SaveChanges. (), перечисляя все измененные объекты и изменяя состояние на удаленное в зависимости от ситуации.
Я решил этот сценарий, настроив ссылочный столбец по мере необходимости и поведение удаления как
Пример:
modelBuilder.Entity<AggregateRoot>()
.HasMany(x => x.Items)
.WithOne()
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
В этом случае EF Core (6.x) больше не задает для столбца ссылки значение
NULL
, но удалил запись, просто удалив
Item
от
Items
сбор сводного корня.
Решающей конфигурацией здесь было поведение удаления
Cascade
.
Почему бы не использовать два репозитория?
var parent = ParentRepo.Get(parentId);
parent.Children.Remove(childId); // remove it from the property Collection
ChildRepo.Delete(childId); // delete it from the database
ParentRepo.Commit(); // calls underlying context.SaveChanges()
Предполагая, что вы делитесь контекстами через IOC/DI, вызов commit с одним репо будет зафиксирован для обоих, в противном случае просто вызовите ChildRepo.Commit
также.