0

I'm developing an MVC application. I have a Domain Model, and I use a repositry pattern for data access and Entity Framework Code First. I also have a UnitOfWork class which I call the repository operations through.

My problem mainly arises when I try to take advantage of aggregate roots and handle child objects through their parent repository.

This is the problem: Parent class "Supplier" has several Contracts with Departments. I've chosen to make the contract a child of Supplier in this case.

To add a new contract I need to add a method to add a contract in my SupplierRepository, I tried:

 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);

            }

And I also tried:

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

         }

When i call

_unitOfWork.save();

I get an error telling me:

An entity object cannot be referenced by multiple instances of IEntityChangeTracker

UnitOfWork instansiates my DbContext (myDbContext) and my SupplierRepository and calls the myDbContext.Save()

  1. Why do I get this behavior
  2. How should I implement an Aggregate Root Repository (CRUD operations for the child objects)

As far as I understand I should have a method in the repository that takes a contract and adds it, and not do this in the Controller of my MVC app, but I don't seem to get it working.

I've seen a lot of information about Aggregate Roots, but no examples of how to implement it.

Thanks.

Solution:

Well I finally figured it out.

So it was not a problem with the repository, but the new SupplierContract queryed the store for the user entity that created it (through an extension method). Obviously this context did not dispose and therefore i had two current DbContexts when I instansiated it to save the contract entity.

Hopefully someone saves time by reading this.

The Aggregate Root repository I solved by just doing like this in the SupplierRepository:

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

And calling UnitOfWork.Save() method.

cfs
  • 1,304
  • 12
  • 30

2 Answers2

2

While technically you may have solved this issue, I do hope you are aware there's something fundamentally flawed in your design (unless you're using Fowler repositories): repositories (the DDD kind) deal in aggregates only. The fact that SupplierContract needs to be added to the context is not a concern of the calling code. So, why expose that method? I would also reconsider having the repository delegate the save (why else have a UoW). As far as aggregates are concerned I get the feeling you seem to be treating them as structural objects, not as behavioral ones. Hence you seem to be in for a world of pain, going through some of the moves, but not getting any of the value.

Yves Reynhout
  • 2,982
  • 17
  • 23
  • Well, the only reason to expose it is that I need to save the contract information (discounts, delivery fees and so on) to the database so the controller gets input from the form and saves it by calling _unitOfWork.SupplierRepository.AddSupplierContract(contractToAdd). You may be right about the structural thinking though, but still i consider the Supplier as the one that offers the contract and therefore as the root. I don't know how else I would do it? – cfs Feb 13 '12 at 23:53
  • 1
    How about aSupplier.AddContract(contractToAdd) where the Supplier aggregate adds it to an internal collection. When save is called for the supplier it should persist-by-reachability the added contract. For an alternative approach, look here: http://stackoverflow.com/questions/8556365/ef-4-2-code-first-and-ddd-design-concerns/8560328#8560328 – Yves Reynhout Feb 14 '12 at 09:39
  • 2
    Hmm, I guess that would seem like a better solution, and also giving the supplier some behavior. Then the aggregate root managed by the SupplierRepository would only persist the state of the supplier. So i then fetch the current Supplier(mySupplier), call mySupplier.AddContract(contractToAdd) and then i call _unitOfWork.SupplierRepository.Update(mySupplier) and save? Sounds like a better solution... – cfs Feb 14 '12 at 11:19
0

To get rid of that error, you should use same MyContext instance to create all the repositories. If you use some dependency-injector, it should allow you to configure same MyContext object through single request. For example, for Ninject, that would be

kernel.Bind<MyContext>().ToSelf().InRequestScope();
archil
  • 39,013
  • 7
  • 65
  • 82
  • That is actually the ide behind UnitOfWork, that they use the same context. Somehow, by trying to add a child in it's parent repository it seems to create a new context... I.e. my repository takes the context from my UnitOfWork class, so It only instansiates one context and keeps it until it is disposed (UnitOfWork implements IDisposable). – cfs Feb 13 '12 at 16:49