Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern for retrieving complex object graphs with Repository Pattern with Entity Framework

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?

like image 592
Darren Lewis Avatar asked Oct 13 '10 09:10

Darren Lewis


People also ask

Do we need Repository pattern with Entity Framework Core?

No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn't useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn't helpful.

Why use Unitofwork?

The unit of work class serves one purpose: to make sure that when you use multiple repositories, they share a single database context. That way, when a unit of work is complete you can call the SaveChanges method on that instance of the context and be assured that all related changes will be coordinated.

What is the use of Repository pattern in c#?

Repository pattern is one of the preferred patterns to apply in an application because it allows programmers to integrate all of the fundamental data operations related to an entity in one main class. Without this pattern, developers would need to create multiple classes for each entity with the same logic.

What is the Repository pattern?

The Repository pattern. Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.


2 Answers

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.

like image 117
RPM1984 Avatar answered Oct 12 '22 23:10

RPM1984


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.

like image 42
Doctor Jones Avatar answered Oct 12 '22 22:10

Doctor Jones