Entity Framework performance improvement: [Section 1]: Different loading mechanisms in Entity Framework

Darshana Mihiran Edirisinghe
5 min readFeb 17, 2023

--

Entity Framework Core is an ORM (Object Relational Mapping) framework that is powerful and widely used for .NET applications. In this article, we are going to discuss different types of loadings available in EF Core.

Recommended Approach: Eager Loading

The Entity Framework supports 3 ways of loading associated data.

  1. Lazy loading
  2. Eager loading
  3. Explicit loading

Lazy Loading

Lazy loading is a feature in Entity Framework Core (EF Core) that allows related entities to be automatically loaded from the database when they are accessed for the first time. With lazy loading, you can defer the loading of related entities until they are actually needed, rather than loading them all at once.

In EF Core, lazy loading is achieved through the use of proxy objects. When an entity is loaded from the database, a proxy object is created in place of the real entity. When a property or navigation property of the entity is accessed, the proxy object intercepts the access and loads the related data from the database.

For example, suppose you have an entity called “Order” that has a navigation property called “Customer”. With lazy loading enabled, when you load an order from the database, the Customer property will initially be a proxy object. When you access the Customer property for the first time, the proxy object will load the related customer data from the database.

To enable lazy loading in EF Core, you can use the “UseLazyLoadingProxies” method when configuring the DbContext. However, it’s important to note that lazy loading can have a negative impact on performance, as it can result in additional database queries being executed. It’s also worth noting that not all navigation properties are eligible for lazy loading, such as properties marked as “virtual” and collection properties.

In summary, lazy loading is a convenient feature in EF Core that allows related entities to be loaded on demand, but it should be used judiciously and with an awareness of its potential impact on performance.

Workaround: Disable lazy loading

To disable lazy loading in Entity Framework Core (EF Core), you can use the UseLazyLoadingProxies method when configuring the DbContext and set it to false. Here's an example.

public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
}

Alternatively, you can disable lazy loading for a specific navigation property by making it non-virtual. For example,

public class Order
{
public int Id { get; set; }
// This property will not be lazy loaded
public Customer Customer { get; set; }
// This property will be lazy loaded
public ICollection<OrderItem> OrderItems { get; set; }
}

In this example, the Customer property is not marked as virtual, so it will not be lazy-loaded. However, the OrderItems property is still marked as ICollection so it will be lazy-loaded by default.

Disabling lazy loading can help improve performance by reducing the number of database queries that are executed. However, it’s important to note that it can also lead to incomplete or unexpected results if related data is not explicitly loaded or included in a query. So, you should only disable lazy loading if you have a good reason to do so and you fully understand the consequences.

Eager Loading

Eager loading is a technique in Entity Framework (EF) that can improve performance by loading related data from the database along with the main entity in a single query, instead of executing additional queries to fetch the related data later.

The ‘Include’ method is the most common way to specify which related entities to load when querying data using EF. This method allows you to specify one or more navigation properties to include in the query. In the below example, the Include method is used to load the Orders related to each Customer in a single query, instead of executing separate queries for each customer.

var customers = context.Customers.Include(c => c.Orders).ToList();

Use the ‘ThenInclude’ method to load related entities that are more than one level deep, you can use the ThenInclude method in combination with Include. In the below example, the ThenInclude method is used to load the OrderItems related to each Order, which is itself related to each Customer.

var customers = context.Customers
.Include(c => c.Orders)
.ThenInclude(o => o.OrderItems)
.ToList();

Use projection to allow you to specify exactly which properties of an entity or related entities to load, instead of loading the entire entity. This can help reduce the amount of data transferred from the database and improve performance. In the below example, the Select method is used to load only the properties that are needed, using anonymous types to project the data.

var customers = context.Customers
.Select(c => new
{
Customer = c,
Orders = c.Orders.Select(o => new
{
Order = o,
OrderItems = o.OrderItems.Select(oi => new
{
OrderItem = oi
})
})
})
.ToList();

Use Compiled Queries to cache the query execution plan and reuse it in future queries. This can help improve performance by reducing the overhead of generating the SQL query and compiling it. In the below example, the CompileQuery method is used to compile a query that loads a Customer by its Id, along with its related Orders.

private static readonly Func<MyDbContext, int, Customer> _customerById =
EF.CompileQuery((MyDbContext context, int id) =>
context.Customers.Include(c => c.Orders).FirstOrDefault(c => c.Id == id));

Explicit loading

Explicit loading is a technique in Entity Framework (EF) that allows you to load related entities on-demand, as opposed to automatically loading them when the main entity is loaded. Explicit loading can help improve performance by reducing the amount of data transferred from the database and reducing the overhead of generating and executing queries.

Use the Entry method to get an object representing the current state of an entity being tracked by the DbContext. You can use the Entry method to load related entities by calling the Collection or Reference method. In the below example, the Entry method is used to load the Orders related to a specific Customer by calling the Collection method.

var customer = context.Customers.Find(1);

context.Entry(customer)
.Collection(c => c.Orders)
.Load();

Use the Load method for loading related entities and is available on the navigation properties of an entity. You can use the Load method to explicitly load related entities. In the below example, the Load method is used to load the Orders related to a specific Customer.

var customer = context.Customers.Find(1);

context.Entry(customer)
.Collection(c => c.Orders)
.Load();

Use filtering to load only the related entities you need. In the below example, the Query method is used to apply a filter to the related Orders, so that only orders from the last 7 days are loaded.

var customer = context.Customers.Find(1);

context.Entry(customer)
.Collection(c => c.Orders)
.Query()
.Where(o => o.OrderDate >= DateTime.Now.AddDays(-7))
.Load();

It’s important to note that explicit loading can lead to additional queries being executed, which can increase the overall response time of your application. So, you should only use explicit loading when it’s necessary and you fully understand the consequences.

--

--

Darshana Mihiran Edirisinghe

Full Stack Developer | Information Technology Consultant | Scrum Master | Tech Enthusiast