views:

91

answers:

2

Hi,

We have an ASP.NET MVC site that uses Entity Framework abstractions with Repository and UnitOfWork patterns. What I'm wondering is how others have implemented navigation of complex object graphs with these patterns. Let me give an example from one of our controllers:

var model = new EligibilityViewModel
   {
       Country = person.Pathway.Country.Name,
       Pathway = person.Pathway.Name,
       Answers = person.Answers.ToList(),
       ScoreResult = new ScoreResult(person.Score.Value),
       DpaText = person.Pathway.Country.Legal.DPA.Description,
       DpaQuestions = person.Pathway.Country.Legal.DPA.Questions,
       Terms = person.Pathway.Country.Legal.Terms,
       HowHearAboutUsOptions = person.Pathway.Referrers
   };

It's a registration process and pretty much everything hangs off the POCO class Person. In this case we're caching the person through the registration process. I've now started implementing the latter part of the registration process which requires access to data deeper in the object graph. Specifically DPA data which hangs off Legal inside Country.

The code above is just mapping out the model information into a simpler format for the ViewModel. My question is do you consider this fairly deep navigation of the graph good practice or would you abstract out the retrieval of the objects further down the graph into repositories?

+3  A: 

It depends on how much of the information you're using at any one time.

For example, if you just want to get the country name for a person (person.Pathway.Country.Name) what is the point in hydrating all of the other objects from the database?

When I only need a small part of the data I tend to just pull out what I'm going to use. In other words I will project into an anonymous type (or a specially made concrete type if I must have one).

It's not a good idea to pull out an entire object and everything related to that object every time you want to access some properties. What if you're doing this once every postback, or even multiple times? By doing this you might be making life easier in the short term at the cost of you're making your application less scalable long term.

As I stated at the start though, there isn't a one size fits all rule for this, but I'd say it's rare that you need to hydrate that much information.

DoctaJonez
+3  A: 

In my opinion, the important question here is - have you disabled LazyLoading?

If you haven't done anything, then it's on by default.

So when you do Person.Pathway.Country, you will be invoking another call to the database server (unless you're doing eager loading, which i'll speak about in a moment). Given you're using the Repository pattern - this is a big no-no. Controllers should not cause direct calls to the database server.

Once a C ontroller has received the information from the M odel, it should be ready to do projection (if necessary), and pass onto the V iew, not go back to the M odel.

This is why in our implementation (we also use repository, ef4, and unit of work), we disable Lazy Loading, and allow the pass through of the navigational properties via our service layer (a series of "Include" statements, made sweeter by enumerations and extension methods).

We then eager-load these properties as the Controllers require them. But the important thing is, the Controller must explicitly request them.

Which basically tells the UI - "Hey, you're only getting the core information about this entity. If you want anything else, ask for it".

We also have a Service Layer mediating between the controllers and the repository (our repositories return IQueryable<T>). This allows the repository to get out of the business of handling complex associations. The eager loading is done at the service layer (as well as things like paging).

The benefit of the service layer is simple - more loose coupling. The Repository handles only Add, Remove, Find (which returns IQueryable), Unit of Work handles "newing" of DC's, and Commiting of changes, Service layer handles materialization of entities into concrete collections.

It's a nice, 1-1 stack-like approach:

personService.FindSingle(1, "Addresses") // Controller calls service
 |
 --- Person FindSingle(int id, string[] includes) // Service Interface
      |
       --- return personRepository.Find().WithIncludes(includes).WithId(id); // Service calls Repository, adds on "filter" extension methods
           |
            --- IQueryable<T> Find() // Repository
                |
                 -- return db.Persons; // return's IQueryable of Persons (deferred exec)

We haven't got up to the MVC layer yet (we're doing TDD), but a service layer could be another place you could hydrate the core entities into ViewModels. And again - it would be up to the controller to decide how much information it wishes.

Again, it's all about loose coupling. Your controllers should be as simplistic as possible, and not have to worry about complex associations.

In terms of how many Repositories, this is a highly debated topic. Some like to have one per entity (overkill if you ask me), some like to group based on functionality (makes sense in terms of functionality, easier to work with), however we have one per aggregate root.

I can only guess on your Model that "Person" should be the only aggregate root i can see.

Therefore, it doesn't make much sense having another repository to handle "Pathways", when a pathway is always associated with a particular "Person". The Person repository should handle this.

Again - maybe if you screencapped your EDMX, we could give you more tips.

This answer might be extending out a little too far based on the scope of the question, but thought i'd give an in-depth answer, as we are dealing with this exact scenario right now.

HTH.

RPM1984
Thanks, your answer has certainly clarified my thinking. The answers so far have helped me realise when I look at my code I'm concerned as to what EF is doing behind the scenes to realise these object graphs. I know that I can spin up SQL Profiler to see what's going on but my initial thinking is that abstracting this out of the controller will give me better control in the future should performance or a requirement to deliver this data to remote sources become a requirement. Your suggestion of a service layer between the controllers and repositories is something I'm going to look into.
Daz Lewis
@Daz Lewis - definetely. We have an API for our website. This is why we have the service layer. Both our Website and API are "clients". This allows a nice, fluent (yet tight) programming model. BTW - you don't HAVE to use SQL Profiler. There is a method on ObjectQuery called .ToTraceString. We use this with logging, all our repository methods debug this line (info level), so we can see what's being executed. Anyway, definetely look into the service layer. Our repositories are very simple - take a look at some of my questions to see how i've done it.
RPM1984
"Controllers should not cause direct calls to the database server", following this then your controller should not call a repository to cause direct calls to the database server either. When accessing a property that is going to be lazy loaded does not cause a direct call to the database server in the same way your repository is causing a direct call to the database server. Yes sure it does cause a call to the database server, but that is indirectly through your infrastructure using an ORM tool.
adriaanp
@adriaanp It's still a round trip (read performance drag) to the database. Your controller shouldn't be causing additional calls to the database (even for lazy loading). They should have all been completed by the time your model was returned from the service layer. This means having a service layer API that is fine grained enough to know exactly what the controller will need.
Ryan
@adriaanp - i was going to reply back, but @Ryan basically tooks the words out of my mouth. Thanks Ryan. :)
RPM1984