Wednesday, May 16, 2018

Entity Framework Core 2.1: Lazy Loading

Last week (to be exact 7th of May, 2018) Entity Framework Core 2.1 RC 1 was released to the public. And one of the features that got released with EF Core 2.1 was Lazy Loading of related data.

In this post, let’s see how you can use that feature.

Consider I have the following code with an EF Core version prior to 2.1.
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Department Department { get; set; }
}
 
public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Employee> Employees { get; set; }
}
 
public class MyDbContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Department> Departments { get; set; }
 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(@"Server=.;Database=EfCoreLazyLoadingDemo;Trusted_Connection=True;");
    }
}
And I have some Departments and Employees in the database.

Departments
image
Departments
Employees
image
Employees
And I have the following code to print out different Departments and Employees of each of the Department.
static void Main(string[] args)
{
    using (MyDbContext context = new MyDbContext())
    {
        foreach (var department in context.Departments)
        {
            Console.WriteLine(department.Name);
            if (department.Employees != null)
            {
                foreach (var employee in department.Employees)
                {
                    Console.WriteLine($"--{employee.Name}");
                }
            }
        }
    }
}
So if you run this, you should be seeing something like below.
image
Output
No Employees were loaded, and to fix that, you will need to do something like below which is known as Eager Loading.
foreach (var department in context.Departments.Include(d => d.Employees))
Basically, the virtual properties have no effect unlike EF.

And to make Lazy Loading work, we need to update EF Core references to 2.1.0-rc1-final. And in addition, we need to install another package, Microsoft.EntityFrameworkCore.Proxies.

After packages are updated/installed, we have two ways.

1. UseLazyLoadingProxies

This is the easiest way, you just need to add UseLazyLoadingProxies to OnConfiguring override (or within AddDbContext).
public class MyDbContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Department> Departments { get; set; }
 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseLazyLoadingProxies()
            .UseSqlServer(@"Server=.;Database=EfCoreLazyLoadingDemo;Trusted_Connection=True;");
    }
}
And now following should be loading each Departments Employees.
foreach (var department in context.Departments)
image
Output
You don’t have to explicitly Include the Employees. But remember, navigational properties should be virtual.

2. Lazy-loading without proxies

This includes polluting your entities with additional code.
public class Employee
{
    private ILazyLoader _lazyLoader { get; set; }
 
    public Employee(ILazyLoader lazyLoader)
    {
        _lazyLoader = lazyLoader;
    }
 
    public int Id { get; set; }
    public string Name { get; set; }
 
    private Department _department;
    public Department Department
    {
        get => _lazyLoader?.Load(this, ref _department);
        set => _department = value;
    }
}
 
public class Department
{
    private ILazyLoader _lazyLoader { get; set; }
 
    public Department(ILazyLoader lazyLoader)
    {
        _lazyLoader = lazyLoader;
    }
 
    public int Id { get; set; }
    public string Name { get; set; }
 
    private ICollection<Employee> _employees;
    public virtual ICollection<Employee> Employees
    {
        get => _lazyLoader?.Load(this, ref _employees);
        set => _employees = value;
    }
}
Now when using this, you don’t need to have UseLazyLoadingProxies and navigation properties doesn’t need to be virtual. EF Core will be injecting ILazyLoader where it’s required, and he is going to load the related data.

This will also give us the following output.
image
Output
And before winding up, note, Lazy Loading should be used with care. It doesn’t matter whether it’s EF or EF Core, it can affect the performance big time.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment