Use Eager, Explicit, and Lazy Loading in Entity Framework Core
You might be aware that Entity Framework Core 2.1 has added support for lazy
loading of navigation properties. This means now we have three ways to load data
in the navigation properties - eager loading, explicit loading, and lazy
loading. This article summarizes all the three options with an example.
To illustrate how these three options can be used we will develop the
following ASP.NET Core MVC application :

The page shown above has a search textbox at the top. You can enter a
CustomerID from Northwind database and click on Show Details button. The page
then displays the customer details along with orders of that customer.
As you might have guessed, a Customer can have one or more Orders. The Orders
are filled in Orders navigation property of the Customer. The following code
shows how the Customer and Order entity classes look like :
[Table("Customers")]
public partial class Customer
{
[Key]
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string Country { get; set; }
[ForeignKey("CustomerID")]
public List<Order> Orders { get; set; }
}
[Table("Orders")]
public class Order
{
public int OrderID { get; set; }
public string CustomerID { get; set; }
public DateTime OrderDate { get; set; }
}
As you can see, the Customer class has Orders navigation property that is
intended to hold all the orders for a customer. Customer and Order entities are
related through the CustomerID property.
The NorthwindDbContext class represents the DbContext and is shown below :
public class NorthwindDbContext:DbContext
{
public NorthwindDbContext
(DbContextOptions<NorthwindDbContext> options)
: base(options)
{
}
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
}
Once the NorthwindDbContext is ready register it with the DI container. This
is done in the ConfigureServices() method of the Startup class.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<NorthwindDbContext>(
options => options.UseSqlServer
("data source=.;initial catalog=Northwind;integrated
security=true;MultipleActiveResultSets=true"));
}
The Index view that displays the customer details including orders is shown
below :
@model EFCoreNavPropDemo.Models.Customer
<form asp-controller="Home" asp-action="Index"
method="post">
<input type="text" name="customerID" />
<button type="submit">Show Details</button>
</form>
@if (Model != null)
{
<h1>Details For CustomerID @Model.CustomerID</h1>
<h2>Company Name : @Model.CompanyName</h2>
<h2>Contact Name : @Model.ContactName</h2>
<h2>Country : @Model.Country</h2>
@if (Model.Orders != null && Model.Orders.Count > 0)
{
<table border="1" cellpadding="10">
@foreach (var item in Model.Orders)
{
<tr>
<td>@item.OrderID</td>
<td>@item.OrderDate</td>
</tr>
}
</table>
}
}
The Index view receives a Customer object as its model and displays customer
details such as CustomerID, CompanyName, ContactName, and Country. If a Customer
has one or more Orders, they are displayed in a table. Every order has OrderID
and OrderDate.
Now that we have models and view ready, it's time to see various navigation
property loading options.
Eager Loading
In eager loading all the orders of a customer are fetched from the database
along with the initial query. This means when you fetch the customer
details from the Customers table, at the same time you will also fetch order
details for that customer. Thus order details are loaded in "eager" way.
[HttpPost]
public IActionResult Index(string customerID)
{
var query = from c in db.Customers.Include
(cust => cust.Orders)
where c.CustomerID == customerID
select c;
return View(query.SingleOrDefault());
}
Have a look at the LINQ to Entities query used by the Index() action. It uses
Include() clause to specify the path of the navigation property to fill in eager
fashion (Orders in this case). The Customer along with its Orders is then
realized using the SingleOrDefault() method and is passed to the Index view.
If you run this application and enter ALFKI as the CustomerID, you should see
customer details and order details as shown in the earlier figure.
Explicit Loading
Now let's use explicit loading instead of eager loading. In explicit loading
the Orders navigation property won't be filled during the initial query. That
means even if you fetch customer details for a customer from the Customers
table, the Orders navigation property will be empty. If you need to access order
details, you will need to explicitly load the Orders navigation property as
shown below :
[HttpPost]
public IActionResult Index(string customerID)
{
var customer = db.Customers.Find(customerID);
db.Entry(customer).Collection(cust => cust.Orders).Load();
return View(customer);
}
The above code fetches a Customer entity using the Find() method. It then
loads Orders for that Customer using Collection().Load() method. The
Collection() method takes the path of the navigation property to load. The
Customer is then passed to the Index view.
Note : If you have 1:1 relation then instead of Collection(...).Load()
you will use Reference(...) .Load()
Lazy Loading
Finally, let's examine lazy loading. In lazy loading Orders navigation
property won't be populated with the initial query. However, when you try to
access Orders of a Customer for the first time, all the Orders for that Customer
are fetched from the database. Obviously, there are going to be more
number of database hits when you use lazy loading. So, you should be using it
with caution. Luckily, lazy loading is not automatically enabled. You
need to enable it either in the Startup class or in the DbContext class. Also,
if you wish to use lazy loading the Orders navigation property must be marked as
a virtual property as shown below :
[Table("Customers")]
public partial class Customer
{
[Key]
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string Country { get; set; }
[ForeignKey("CustomerID")]
public virtual List<Order> Orders { get; set; }
}
You also need to add Microsoft.EntityFrameworkCore.Proxies NuGet package to
your project.

In our example we will enable lazy loading in the Startup class. Here is how
that can be done:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<NorthwindDbContext>(options => options
.UseSqlServer
("data source=.;initial catalog=Northwind;integrated
security=true;MultipleActiveResultSets=true")
.UseLazyLoadingProxies());
}
As you can see, we have called UseLazyLoadingProxies() method while adding
the NorthwindDbContext. Your Index() action will now look like this :
[HttpPost]
public IActionResult Index(string customerID)
{
var customer = db.Customers.Find(customerID);
return View(customer);
}
As you can see, we haven't done anything special to the Customer entity under
consideration. When your code accesses Orders navigation property for the first
time, orders for that customer will be populated from the database.
That's it for now! Keep coding!!