views:

134

answers:

2

I'm in the process of building my first web application using ASP.NET MVC 2 and the Entity Framework. I am using the repository pattern. From information I have gathered on Stack Overflow and other websites it appears that the consensus is to have one one controller per domain model object and one repository per aggregate root object.

The question I have is how I should be accessing non-root aggregate objects through the aggregate root repository. In the example I am dealing with there is a Customer model and a Boat model. Boats can only exist with a FK reference to a Customer and Boats only need to be referenced within the context of a Customer. This led me to believe that I had an aggregate with these two objects and that the Customer was the root.

Now in my Boats controller I have an Edit action method:

public class BoatsController : Controller
{
    private ICustomersRepository customersRepository;
    public BoatsController(ICustomersRepository customersRepository)
    {
        this.customersRepository = customersRepository;
    }   
    public JsonResult Edit(int id, FormCollection collection)
    {
        var boat = customersRepository.GetBoat(id);
        // Update boat
    }
}

My question is how do I want to retrieve the Boat object in the repository?

public class SQLCustomersRepository : ICustomersRepository
{
    DatabaseEntities db = new DatabaseEntities();

    public Boat GetBoat(int id)
    {
        return db.Boats.SingleOrDefault(x => x.ID == id);

        // OR

        return db.Customers.Where(x => x.Boats.Any(y => y.ID == id)
                           .Select(x => x.Boats.FirstOrDefault(y => y.ID == id);
    }
}

Is it acceptable for me to reference db.Boats from the Customers Repository? It seems to be the cleanest, but it would mean that I would have to alter my FakeCustomersRepository to have a List of Customers AND Boats.

It seemed more reasonable to me to access the Boat through db.Customers, however I couldn't figure out how to return a single Boat object without repeating myself in the LINQ query which really didn't sit well with me.

I know I still have a lot to learn so hopefully you can point me in the right direction!

Thanks!

A: 

Why don't you have a Boat repository?

If Boat is an AggRoot then you should have a separate repository for it.

jfar
Boats are never referenced outside the context of a specific Customer. I was pretty sure that this made the Customer the root of the Boat and Customer aggregate. Perhaps I'm not understanding how to define the aggregate root properly?
Terminal Frost
+1  A: 

In my opinion you are talking about implementation details, not about your abstract interfaces and your model. Your interface seems to look like this:

public interface ICustomersRepository
{
    //... other methods
    Boat GetBoat(int id);
    //... other methods
}

and the model like this:

public class Customer
{
    //... other stuff
    ICollection<Boat> Boats; // or another collection type
    //... other stuff
}

I my opinion it isn't against any design rules to implement the ICustomerRepository completely different for your SQLCustomersRepository and for your FakeCustomersRepository and to find an implementation which is optimal with respect to your underlying data storage.

Since you seem to have a primary key on the Boat table in your SQL-DB I would definitely take advantage of this and implement the first option: return db.Boats.SingleOrDefault(x => x.ID == id);

But it's not necessary to have a Boat collection in your FakeCustomersRepository. If it is easier for you just to have a list of Customers and to populate the Boats collection in each Customer with test boats, why not? In this case your GetBoat implementation in the FakeCustomersRepository could be implemented with the second LINQ query you described.

Keep in mind that Unit testing in MVC doesn't intend to test your specific Repository implementations but your business logic, and a business action is more a chain of various domain object and repository method calls. If a single repository method does what it is supposed to do (for instance if GetBoat really returns a boat with the correct ID) is more a matter of a later integration test to prove that your database calls work properly.

Slauma
Thanks for the detailed reply. Since I'm new at this I wanted to make sure I wasn't violating some cardinal rule or anything.
Terminal Frost