Working with the repository pattern to eager load entities using ThenInclude in Entity Framework, we can achieve efficient data retrieval and minimize the number of database round trips.
We are going to implement the repository pattern, which allows us to pass our class and context into a general repository, and enables us to specify 'include' statements.
Firstly, we define our repository interface with methods for retrieving entities and eagerly loading related entities:
public interface IRepository<T>
{
IQueryable<T> GetAll();
IQueryable<T> GetAllIncluding(params Expression<Func<T, object>>[] includeProperties);
}
Next, we implement the repository interface in our concrete repository class:
public class Repository<T> : IRepository<T> where T : class
{
private readonly AppDbContext _context;
private readonly DbSet<T> _dbSet;
public Repository(AppDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public IQueryable<T> GetAll()
{
return _dbSet;
}
public IQueryable<T> GetAllIncluding(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _dbSet;
return includeProperties.Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
}
}
Now, we can utilize the repository to eager load related entities using ThenInclude in our service layer:
public class ProductService
{
private readonly IRepository<Product> _productRepository;
public ProductService(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public List<Product> GetProductsWithCategories()
{
return _productRepository.GetAllIncluding(p => p.Category).ToList();
}
}
In this example, we're eager loading the Category entity related to each Product entity using ThenInclude. This allows us to retrieve all products along with their associated categories in a single database query, optimizing performance and reducing overhead.
Below are the model classes for Product and Category:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
In the Product class, we define properties Id, Name, Price, and a navigation property for the related Category entity. Similarly, in the Category class, we define properties like Id, Name, and a navigation property for the collection of related Product entities.
Below is the DbContext class:
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
In this DbContext class, we define DbSets for the Product and Category entities, allowing us to query and manipulate data in these tables, the constructor initializes the DbContext with the provided options, and the OnModelCreating method can be used to configure relationships, constraints, and other aspects of the database schema.