Шаблон репозитория: удаление совокупного корня

При удалении модели (корня агрегата) из хранилища также должны быть удалены все связанные агрегаты.

Я пытаюсь реализовать это в моей реализации Entity Framework 6 шаблона хранилища

В моем примере я хочу удалить Customer от CustomerRepository, Все клиенты Order объекты также должны быть удалены.

Репозиторий (урезанный):

public interface IRepository<T> where T : DomainEntity
{
    void Remove(T item);       
}

public class EntityFrameworkRepository<T> : IRepository<T> where T : DomainEntity
{
    private readonly DbSet<T> dbSet;
    public DbContext context;

    public EntityFrameworkRepository(IUnitOfWork unitOfWork)
    {
        context = entityFrameworkUnitOfWork.context;
        dbSet = dbSet = context.Set<T>();
    }

    public virtual void Remove(T item)
    {
        DbEntityEntry dbEntityEntry = context.Entry(item);

        if (dbEntityEntry.State == EntityState.Detached)
        {
            dbSet.Attach(item);
        }

        dbSet.Remove(item);
    }
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    public readonly DbContext context;

    public EntityFrameworkUnitOfWork()
    {
        this.context = new ReleaseContext();
    }

    public void Commit()
    {
        context.SaveChanges();
    }
}

ICustomerRepository а также CustomerRepository (Реализация EF):

public interface ICustomerRepository : IRepository<Customer>
{
    IEnumerable<Customer> GetAllActive();
}

public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository
{
    public CustomerRepository(IUnitOfWork unitOfWork)
        : base(unitOfWork)
    { }

    public override void Remove(Order item)
    {
        item.Orders.Clear();

        base.Remove(item);
    }
}

Клиент-код:

customerRepository.Remove(customer);
unitOfWork.Commit();

Исключение:

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

Я бы кроме звонка item.Orders.Clear() указать EF, что ассоциации должны быть удалены.

2 ответа

Решение

Есть хорошая практика: ничего не удаляйте:) Пометьте его как "удаленный".

Из-за чего? Покажите нам реальное требование бизнеса для физического удаления материала? Это не только замедляет работу (обычно БД блокируется при удалении), вызывает фрагментацию и т. Д., Но в большинстве случаев это абсурд! Ни один бизнес не позволит вам физически удалить клиента и список заказов!

Бизнес ничего не удаляет. В реальном бизнесе никто не найдет все документы, связанные с конкретным клиентом, и избавится от них в шредере. Если они не сделали что-то незаконное и ФБР стучится в дверь:)

Поговорите со своими бизнес-экспертами, которые немного разбираются в компьютерах (это настоящие бизнес-эксперты). Они расскажут вам, что происходит с клиентами, когда они перестают быть клиентами (возможно, они "заархивированы", возможно, что-то еще, или, возможно, просто ничего), а затем смоделируют это. Именно мы, программисты, обычно придумываем концепцию "удаления" материала.

Кроме того, анализ исторической информации может быть действительно полезным в будущем!

Есть только два варианта, когда физическое удаление может быть необходимо:

  1. Чтобы сэкономить место на диске (что больше не проблема, когда место на диске так же дешево, как грязь)
  2. Иметь юридическое обязательство физически удалять данные, когда клиент хочет, чтобы они были удалены (что является очень редким требованием и обычно выполняется в определенных доменах).

Для #1, опять же, пространство не является проблемой в наши дни, поэтому реализация удаления может стоить больше, чем выгода от него. Для #2 вы все равно хотите быть явным и, вероятно, вы бы по-другому управляли своим хранилищем данных. Например, вы можете захотеть иметь БД для каждого клиента, чтобы вы могли просто удалить БД и все резервные копии, чтобы они соответствовали правилам (да, вы должны удалить резервные копии, чтобы по закону сказать, что вы больше не храните удаленные данные)

Так какой у тебя случай? Почему вы хотите удалить, каковы ваши реальные бизнес- требования?

Ошибка предполагает, что ваш метод clear не удалил дочерние объекты.
Известно ли вам о каскаде плавного добавления API при удалении?

HasRequired (t => t.Parent).WithOptional (). WillCascadeOnDelete (true);

Таким образом, если вы удалите корневой объект, все зависимые могут быть удалены с помощью Db. Хотя этот вариант не всегда доступен...

Поскольку вы используете IRepository... вы рассматривали возможность использования некоторого шаблона, как

public int DeleteWhere(Expression<Func<TPoco, bool>> predicate) {
        var delList = Context.Set<TPoco>().Where(predicate);
        foreach (var poco in delList) {
            SetEntityState(poco, EntityState.Deleted);
        }
        return delList.Count;

    }

var custid = 1;   
var RepOrder - new Respository<Order>();
var delcnt = RepOrder.DeleteWhere(t=>t.CustomerId == custid);

MYContext.SaveChanges()