I’ve been using Dapper as my data access ORM of choice these days just because I find it far easier than working out the right combination of Fluent Mappings that will actually get me what I want – so unbeknown to me, EF Core didn’t have Lazy Loading functionality up until version 2.1. And even now, it’s less intuitive than what you might think.
This is less of a tutorial and more of a “if you are banging your head against a wall, here’s how to fix that”. You should already be up and running with Entity Framework with all your migrations going smoothly etc. This is just a “Here’s how do to Lazy Loading in Entity Framework Core if you need it” type deal.
First, EF Core by itself doesn’t have lazy loading functionality out of the box. You are first required to install another package. So in your project, you will need to run the following in your package manager console :
Install-Package Microsoft.EntityFrameworkCore.Proxies
I would highly recommend that you install the exact same version of the Proxies package as the EntityFramework version you are using. Otherwise when you install the Proxies project, it’s going to upgrade your EF version at the same time which in the majority of cases you want to avoid.
Next, depending on where you are configuring your DB Context. If it’s in your startup.cs file, then you’re just going to want to make a call to UseLazyLoadingProxies() like so :
services.AddDbContext<MyContext>(options => { options.UseSqlServer().UseLazyLoadingProxies(); }
If you are configuring everything in your actual context, then you can basically do the same thing with the options builder :
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseLazyLoadingProxies(); }
Now when you run your project, 9 times out of 10 you will get :
InvalidOperationException: Navigation property ‘NavProperty’ on entity type ‘Entity’ is not virtual. UseLazyLoadingProxies requires all entity types to be public […]
This is because all navigation properties must be marked as virtual, so for example I would have to change my class to be :
public class MyEntity { [...] public virtual AnotherEntity MyNavigationProperty {get;set;} }
The virtual keyword allows EF to override the property and fetch the data when called. This is slightly different from Entity Framework in full .NET. From memory you could not mark things as virtual if you wanted to have a navigation property only when using the “Include” with your LINQ. But here’s it’s basically enforced.
If you turn on Lazy Loading, all navigation properties (e.g. Non Primitives), are required to be virtual.
For me it somewhat makes sense. And if you are worried about people using a navigation property that will load an incredible amount of data, then it basically boils down to a few things :
- Don’t use Lazy Loading at all
- Remove navigation properties on joins that could cause a large data load
- Write your repository layer in a way that encourages the use of Include statements/Eager loading wherever possible, and Lazy Loading is the exception to the rule.
Microsoft also have a way to Lazy Load without the use of the proxies package. But it looks pretty over the top and you are likely better off just eager loading instead.