Lazy loading is a very cool design pattern that is not often talked about. Many frameworks implement it without even telling us about it 😊. For example, .NET Entity Framework (all versions) support the pattern.
But what is lazy loading? The idea behind the pattern is simple. By using lazy loading, we defer the initialization of objects (heavy ones) until we need them. That’s it.
Let’s provide an example. We have a simple HR application. The application supports the tracking of employee records. Each record has standard fields like name, email, date of birth, and image (like a profile picture), etc.
The application has operations like adding employees, updating employees, searching for an employee, generating reports, etc. Do all functions need the image of the employee? Should we load images in some aggregated reports? Of course not. A picture can take a lot of resources, so it is not reasonable to load it everywhere. And here, lazy loading comes to help us.
Lazy loading implementation
There are several ways to implement the lazy loading pattern, but we are going to inspect the most used one (when it comes to manual implementation).
In the source code, the employee domain model is represented like this:
public class Employee
{
public string Name { get; set; }
public string Email { get; set; }
public byte[] Image { get; set; }
public Employee()
{}
public void Load()
{
// LOGIC LOADING ALL THE DATA.
}
}
Nothing special. Just a class with three fields and a method which would load all the data. But as we said, most of the time, we do not want the Image data because it could take a lot of resources.
So, how do we refactor the class? We do It like this:
public class Employee
{
private byte[] image;
public string Name { get; set; }
public string Email { get; set; }
public byte[] Image
{
get {
if (image == null)
{
image = LoadImage();
return image;
}
else
{
return image;
}
}
set {
image = value;
}
}
public Employee()
{}
public void Load()
{
// LOGIC LOADING ONLY NAME AND EMAIL PROPERTIES.
}
public byte[] LoadImage()
{
// LOGIC LOADING AND RETURNING IMAGE DATA.
}
}
We followed only four steps:
- Extract the logic for image data load from the Load method to a new Method – LoadImage;
- Introduce a new private field for the image data;
- Refactor the Image property to check if image data is null. If it is, the new method loads the image data, assigns it to the image field, and then return it to the consumer.
Next time we load the object (or objects if we load a collection of them), we won’t have the overhead of loading unnecessary image data. We are going to extract the image data only when we need it (by accessing Image property).
ORMs
ORMs like Entity Framework Core support lazy loading. When a query is executed, not all the properties are loaded (unless you have disabled lazy loading). Only by accessing them, another query runs to fetch the data.
For example, imagine we have two entities in relation: a Company entity and an Employee entity. A company has many employees. If we query for a specific company by id and we get a result, the list of its employees won’t be loaded. Only when we access them a new query will fetch the collection.
In some cases, this is cool in others, not 😊.
Can we implement our own lazy loading if we are using some ORM? Sure. The thing you should do is not to map properties/entities that don’t need to be loaded with every query. Of course, you should provide proper methods to handle the data that is not loaded.
In Entity Framework, you use the [NotMapped] attribute to annotate desired properties.
You can read in many places that lazy loading is used when accessing network resources. That is true but not complete. You can use lazy loading also for any demanding operation, like complex computations, reading big chunks of local data, etc.