Generic Repository : Eager load In Entity Framework Using "OrderBy" and "Include"


While working on my project, I encountered the need to implement the repository pattern, providing a flexible and efficient approach to accessing data within my application. I faced a challenge when trying to utilize the Get and Find methods of the repository in a manner that allowed me to specify ordering, filtering, and eager loading of related entities.

In this blog post, will create Generic repository with eager load using ThenInclude(),OrderBy() method, I will share my journey of tackling this challenge and discuss the solutions I discovered along the way. We'll explore how to implement the repository pattern effectively, allowing us to pass our class and context into a general repository and utilize the Get and Find methods with 
"order" and some "include" statements.
 

To Show you the example,let's first create the ProfessorDbContext class representing our database context:


        public class ProfessorDbContext : DbContext
        {
            public DbSet<Professor> Professors { get; set; }
            public DbSet<Course> Courses { get; set; }

            public ProfessorDbContext(DbContextOptions<ProfessorDbContext> options) : base(options)
            {
            }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                
            }
        }
    

Here, we define the Professor class representing the Professor table:


        public class Professor
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Email { get; set; }
            public string Address { get; set; }
            public string DepartmentName { get; set; }       
            public ICollection<Course> Courses { get; set; }
        }
    

        public class Course
        {
            public int Id { get; set; }
            public string Title { get; set; }
            public string Description { get; set; }
            public int ProfessorId { get; set; }
            public Professor Professor { get; set; }
        }
    

In the Professor class, we define properties such as Id, Name, Email, Address, and DepartmentName. Additionally, we include a navigation property Courses to represent the relationship with the Course table.

Now, let's write the GenericRepository to work with the ProfessorDbContext and Professor table:


        public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
        {
            private readonly ProfessorDbContext _context;

            public GenericRepository(ProfessorDbContext context)
            {
                _context = context;
            }

            public IQueryable<TEntity> GetAll()
            {
                return _context.Set<TEntity>().AsNoTracking();
            }

            public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate, 
                                                 Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, 
                                                 params Expression<Func<TEntity, object>>[] includeProperties)
            {
                IQueryable<TEntity> query = _context.Set<TEntity>().AsNoTracking();

                if (includeProperties != null)
                {
                    foreach (var includeProperty in includeProperties)
                    {
                        query = query.Include(includeProperty);
                    }
                }

                if (orderBy != null)
                {
                    query = orderBy(query);
                }

                return query.Where(predicate);
            }
        }
    

In GenericRepository class, we inject the ProfessorDbContext , allowing us to work with the Professor table. The GetAll method retrieves all entities from the Professor table, while the Find method enables filtering, ordering, and eager loading of related entities.

With these modifications, our GenericRepository is now work with the ProfessorDbContext and Professor table, providing a generic data access layer for our application.

Let's let's extend the usage of the GenericRepository to demonstrate filtering and ordering using the Find method in our ProductController:


        public class ProductController : ControllerBase
        {
            private readonly IGenericRepository<Professor> _professorRepository;

            public ProductController(IGenericRepository<Professor> professorRepository)
            {
                _professorRepository = professorRepository;
            }

            [HttpGet]
            public IActionResult GetProfessors()
            {
                var professors = _professorRepository.GetAll().ToList();
                return Ok(professors);
            }

            [HttpGet("{id}")]
            public IActionResult GetProfessor(int id)
            {
                var professor = _professorRepository.Find(p => p.Id == id).FirstOrDefault();
                if (professor == null)
                {
                    return NotFound();
                }
                return Ok(professor);
            }

            [HttpGet("filtered")]
            public IActionResult GetFilteredProfessors(string department, string orderBy)
            {
                IQueryable<Professor> query = _professorRepository.Find(
                    predicate: p => string.IsNullOrEmpty(department) || p.DepartmentName == department,
                    orderBy: orderBy switch
                    {
                        "Name" => q => q.OrderBy(p => p.Name),
                        "Email" => q => q.OrderBy(p => p.Email),
                        _ => null
                    }
                );
                var professors = query.ToList();
                return Ok(professors);
            }
        }
    

In ProductController, we've added action methods:

  • GetProfessors:Retrieves professor's
  • GetProfessor: Retrieves a single professor by their ID.
  • GetFilteredProfessors: Retrieves professors filtered by department and ordered based on the specified criteria (Name or Email).

Using GenericRepository's Find method in performing filtering, ordering, and retrieval of entities based on specific criteria.