Как реализовать репозиторий Aggregate Root и добавить дочерний объект с EF

Я разрабатываю приложение MVC. У меня есть модель домена, и я использую шаблон репозитория для доступа к данным и Entity Framework Code First. У меня также есть класс UnitOfWork, через который я вызываю операции с репозиторием.

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

Это проблема: у родительского класса «Поставщик» есть несколько контрактов с отделами. В этом случае я решил сделать контракт дочерним по отношению к поставщику.

Чтобы добавить новый контракт, мне нужно добавить метод для добавления контракта в мой SupplierRepository, я попробовал:

 public class SupplierRepository : GenericRepository<Supplier> 
        {

            public SupplierRepository(MyContext context) 
            : base(context)
            {
            }

            public void AddSupplierContract(SupplierContract contract)
            {
                 var supplier = context.Suppliers.Find(contract.SupplierId);
                 supplier.Contract.Add(contract);

            }

И еще я пробовал:

            public void AddSupplierContract(SupplierContract contract)
            {
                context.Entry(contract).State = EntityState.Added;
            }

         }

Когда я звоню

_unitOfWork.save();

Я получаю сообщение об ошибке:

На объект сущности не могут ссылаться несколько экземпляров IEntityChangeTracker

UnitOfWork создает мой DbContext (myDbContext) и мой SupplierRepository и вызывает myDbContext.Save ()

  1. Почему у меня такое поведение
  2. Как мне реализовать агрегированный корневой репозиторий (операции CRUD для дочерних объектов)

Насколько я понимаю, у меня должен быть метод в репозитории, который берет контракт и добавляет его, а не делает это в контроллере моего приложения MVC, но мне кажется, что он не работает.

Я видел много информации об агрегированных корнях, но не видел примеров того, как это реализовать.

Спасибо.

Решение:

Ну наконец-то я разобрался.

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

Надеюсь, кто-то сэкономит время, прочитав это.

Репозиторий Aggregate Root, который я решил, просто сделав это в SupplierRepository:

    public void AddSupplierContract(SupplierContract contract)
    {
        db.SupplierContracts.Add(contract);
    }

И вызов метода UnitOfWork.Save ().




Ответы (2)


Хотя технически вы, возможно, решили эту проблему, я все же надеюсь, что вы знаете, что в вашем дизайне есть что-то принципиально неправильное (если вы не используете репозитории Фаулера): репозитории (типа DDD) работают только в совокупности. Тот факт, что SupplierContract необходимо добавить в контекст, не является проблемой для вызывающего кода. Итак, зачем раскрывать этот метод? Я бы также пересмотрел вопрос о том, чтобы репозиторий делегировал сохранение (зачем еще использовать UoW). Что касается агрегатов, мне кажется, что вы относитесь к ним как к структурным объектам, а не как к поведенческим. Следовательно, вам кажется, что вас ждет мир боли, когда вы делаете некоторые движения, но не получаете никакой пользы.

person Yves Reynhout    schedule 13.02.2012
comment
Что ж, единственная причина для его раскрытия заключается в том, что мне нужно сохранить информацию о контракте (скидки, сборы за доставку и т. Д.) В базу данных, чтобы контроллер получал ввод из формы и сохранял его, вызывая _unitOfWork.SupplierRepository.AddSupplierContract (contractToAdd) . Возможно, вы правы насчет структурного мышления, но я все же считаю Поставщика тем, кто предлагает контракт и, следовательно, корнем. Не знаю, как бы еще я это сделал? - person cfs; 14.02.2012
comment
Как насчет aSupplier.AddContract (contractToAdd), где агрегат Supplier добавляет его во внутреннюю коллекцию. Когда для поставщика запрашивается сохранение, он должен сохранять добавленный контракт для обеспечения доступности. Альтернативный подход можно найти здесь: stackoverflow.com/questions/8556365/ - person Yves Reynhout; 14.02.2012
comment
Хм, я полагаю, это могло бы показаться лучшим решением, а также определило бы поведение поставщика. Тогда совокупный корень, управляемый SupplierRepository, сохранит только состояние поставщика. Затем я беру текущего поставщика (mySupplier), вызываю mySupplier.AddContract (contractToAdd), а затем вызываю _unitOfWork.SupplierRepository.Update (mySupplier) и сохраняю? Похоже на лучшее решение ... - person cfs; 14.02.2012

Чтобы избавиться от этой ошибки, вы должны использовать один и тот же экземпляр MyContext для создания всех репозиториев. Если вы используете какой-либо инжектор зависимостей, он должен позволить вам настроить один и тот же объект MyContext с помощью одного запроса. Например, для Ninject это будет

kernel.Bind<MyContext>().ToSelf().InRequestScope();
person archil    schedule 13.02.2012
comment
На самом деле идея UnitOfWork заключается в том, что они используют один и тот же контекст. Каким-то образом, пытаясь добавить дочерний элемент в родительский репозиторий, создается впечатление, что создается новый контекст ... мой репозиторий берет контекст из моего класса UnitOfWork, поэтому он создает только один контекст и сохраняет его до тех пор, пока он не будет удален (UnitOfWork реализует IDisposable). - person cfs; 13.02.2012