Eager Load Navigation Properties By Default In EF Core

Normally when loading navigation properties in EF Core, you’re forced to use the “Include” method to specify which navigational properties to pull back with your query. This is a very good practice because it means you are explicitly saying what pieces of data you actually require. In fact, up until EF Core 2.1, there wasn’t even an option to use Lazy Loaded entities (Although if you do want to do that, we have a guide on that here : https://dotnetcoretutorials.com/2019/09/07/lazy-loading-with-ef-core/ ).

Just as an example of how you might use the “Include” method,  let’s imagine I have two classes. One called “Contacts”, and one called “ContactEmail”.

class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection ContactEmails { get; set; }
}

class ContactEmail
{
    public int ContactId { get; set; }
    public Contact Contact { get; set; }
    public string Email { get; set; }
}

With EF Core code first, this navigational property would be handled for us based on conventions, no problem there. When querying Contacts, if we wanted to also fetch the ContactEmails at the same time, we would have to do something like so :

_context.Contact.Include(x => x.ContactEmails)
                .FirstOrDefault(x => x.Id == myContactId)

This is called “Eager Loading” because we are eagerly loading the emails, probably so we can return them to the user or use them somewhere else in our code.

Now the problem with this is what if we are sure that *every* time we load Contacts, we want their emails at the same time? We are certain that we will never be getting contacts without also getting their emails essentially. Often this is common on one-to-one navigation properties, but it also makes sense even in this contact example, because maybe everywhere we show a contact, we also show their emails as they are integral pieces of data (Maybe it’s an email management system for example).

AutoInclude Configuration

Up until EF Core 5, you really had no option but to use Includes. That’s changed with a very undocumented feature that has come in handy for me lately!

All we need to do is go to our entity configuration for our contact, and do the following :

builder.Navigation(x => x.ContactEmails).AutoInclude();

To be honest, I’ve never really used the Navigation configuration builder, so didn’t even know it exists. And it’s important to distinguish that you cannot write AutoInclude() on things like HasOne() or HasMany() configurations, it has to stand on it’s own like above.

And.. That’s it! Now every time I get Contacts, I also get their ContactEmails without having to use an Include statement.

Ignoring AutoInclude

Of course, there are times where you opt into AutoInclude and then the very next day, you want to write a query that doesn’t have includes! Luckily, there is a nice IQueryable extension for that!

 _context.Contact.IgnoreAutoIncludes()
    .FirstOrDefault(x => x.Id == myContactId)

Here we can easily opt out so we are never locked into always having to pull back from the database more than we need!

ENJOY THIS POST?
Join over 3,000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

6 comments

  1. For anyone else that was slightly confused on where to put the AutoInclude code, it has to be placed in the OnModelCreating method in your DbContext class.

    The code also has to be modified to modelBuilder.Entity.Navigation(x => x.ContactEmails).AutoInclude();

  2. Good day Sir Wade, thank you for the AutoInclude() function trick, I tried it and it worked like charm. However, it fails if used on a nested navigation property as follows:

    entity.Navigation(e => e.Address.City).AutoInclude();

    I get the following error:

    System.ArgumentException: ‘The expression ‘e => e.Address.City’ is not a valid member access expression. The expression should represent a simple property or field access: ‘t => t.MyProperty’. (Parameter ‘memberAccessExpression’)’

    I totally understand the error and that I should not attempt to access nested navigation properties: I have done it this way initially before coming across your article:

    await repository.VaccinationPoint.GetListAsync(
    include: e => e.Include(i => i.Address).ThenInclude(i => i.City));

    My question to you and the community of this blog is, how can I get past the error above and have AutoInclude work with nested navigation properties?

    1. Hey there, I honestly haven’t tried it, but I think your best bet would be to set up the auto include on your entity to load the address. Then when you configure the Address, set it up to auto include city. It *should* cascade down like that.

      1. Thank you very much for your swift, kind and helpful response. I guess it make more sense this way and thanks for the lesson. Moving these dependencies to the dBContext class is much more cleaner and clearer.

  3. I was using Automapper with Generic classes (yes, I’m a DRY maniac) but had to ditch the idea after finding that navigation properties didn’t load (unless specified with Include method). I just want to confirm that this has worked for me and now I just reduced (and avoided future) lots of code. This is a 100% time saver, Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *