0

I have a long-time burning question about how to avoid null errors with data queried via Entity Framework (version 6 - not Core yet, sadly).

Let's say you have a table Employees, and it has a relationship with another table, EmployeePayments (one employee has many employee payments).

On your Employee domain object you create a property TotalPayments which relies on you having loaded the EmployeePayments for that object.

I try to ensure that any time I do a query, I "include" the dependency, for example:

var employees = context.Employees.Include(e => e.EmployeePayments);

The problem is, I have a lot of queries around the place (I use the generic repository pattern, so I call repository functions like GetAll or GetSingle from my service library), and so that's a lot of places to remember to add the includes. If I don't include them, I run the risk of having a null exception if the TotalPayments property is used.

What's the best way to handle this?

Note 1: we have a lot of tables and I don't really want to have to revert to using specific repositories for each one, we take advantage of the generic repository in a lot of ways.... but I will be happy to hear strong arguments for the alternative :)

Note 2: I do not have lazy loading turned on, and don't plan on turning it on, for performance reasons.

4
  • Is your question how to fix TotalPayments property to avoid null exceptions, or how to be able to set includes with generic repository pattern? Commented Sep 30, 2020 at 0:25
  • In EF Core, all queries flow through github.com/dotnet/efcore/blob/…. There's probably a way to hook a pre-processor into the pipeline to apply conventions... Commented Sep 30, 2020 at 4:20
  • Can you call .Metadata.PrincipalToDependent.SetIsEagerLoaded(true) on the definition of the navigation property? (idea from stackoverflow.com/questions/54041802/…) Commented Sep 30, 2020 at 4:36
  • That looks interesting @JeremyLakeman, I'll see what I can achieve with that. Commented Oct 1, 2020 at 6:25

1 Answer 1

1

This is one reason I consider the Generic Repository an anti-pattern for EF. I use a repository pattern, but scope it like I would a controller. I.e. a CreateOrderController would have a CreateOrderRepository. This repository would provide access to all relevant entities via IQueryable. Common stuff like lookups etc. would have their own secondary repository. Using generic repositories that are geared to working with a single entity type mean adding references to several repositories to do specific things and running into issues like this when attempting to load entities. Sometimes you want related data, sometimes you don't. Simply adding convenient methods in top level entities effectively "breaks" that an object should always be considered complete or complete-able without relying on lazy-loading which brings significant performance costs.

Having repositories return IQueryable avoids many of the problems by giving control to the calling code how entities are consumed. For instance I don't put helper methods in the entities, but rather code needing to populate a view model relies on Linq to build the view model. If my view model wants a sum of payments for an employee, then my repository returning IQueryable can do the following:

public IQueryable<Employee> GetEmployeeById(int employeeId)
{
    return Context.Employees.Where(x => x.EmployeeId == employeeId);
}

then in the controller / service:

using (var contextScope = ContextScopeFactory.Create())
{
    var employeeViewModel = EmployeeRepository.GetEmployeeById(employeeId)
        .Select(x => new EmployeeSummaryViewModel
        {
           EmployeeId = x.EmployeeId,
           EmployeeName = x.LastName + ", " + x.FirstName,
           TotalPayments = x.Payments.Where(p => p.IsActive).Sum(p => p.Amount)
       }).Single();
}

I use a repository because it is easier to mock out than the DbContext and it's DbSets. For Synchronous code I just have the mock to populate and return List<Employee>().AsQueryable(). For Async code I need to add a wrapper for an Async List.

This pattern may go against more traditional views of a repository and separation of concerns that the calling code needs to "know" about the entities, that EF-isms are leaked. However, no matter what approach you try to rationalize to get around the inefficiencies of trying to "hide" EF behind a repository, either you will be left with very inefficient code where repositories return pre-populated DTOs or contain dozens of near identical methods to return different DTOs (or worse, entities in various degrees of completeness) or you are adding complexities like passing in magic strings or expression trees into your methods to tell EF how to filter, how to sort, what to include, paging, etc. Passing in expressions or strings requires the calling code to "know" about the entities and leaks EF restrictions. (Passed in expressions / strings still have to be able to be ultimately understood by EF)

So this may not be a viable answer to the current state of your project, but it might be worth looking into whether your dependency on the repositories can be better managed without splitting them with the Generic pattern, and/or leveraging EF's excellent IQueryable / Linq capabilities to let your controllers/services project the entities into view models / DTOs rather than embedding these reduce elements in the entities themselves.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, you've given me a lot to think about! I agree about generic repository being an anti-pattern, and have also read a lot about that over the years. However it can be so helpful both early in an MVP build (when you just want to access your repository without having to create one per entity) AND as the project grows and you have so many entities, so I'm always split.
I use IQueryable through my code for things like filtering and pagination, but otherwise I tend to do the query in the repo itself to avoid exactly what I'm trying to avoid here, leaving it up to the caller to know about what they need to include. Perhaps the half-way solution is to have specific repositories for entities like this which DO have specific dependencies, since they also tend to be our "big boys" -- the entities that drive the whole system and deserve more personalised love :)
"leaving it up to the caller to know about what they need to include." Ultimately it is the caller that needs to know what is to be included though. I know having properties exposed by entities to effectively Reduce normalized data is tempting but your options to provide that is either: A) the caller needs to know to eager load to use that reduction property (in which case it may as well know how to extract it via IQueryable) B) Eat the performance cost of Lazy Loading (usually unacceptable) or C) Eat the performance/resource bloat of eager loading always. IMHO A) is the better option.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.