EF Core: How to load data from a table and related tables in Generic Repository

I created a Generic Repository that can help me in my projects.

The problem:

In these days I work on a project and I want to get data from some related tables (all data from one table and also load all related data from other tables that I select), the repository I was created can’t handle this also I don’t want to work directly with Entity Framework Core.

My repository:

My repository is an open source project, you can see the source code from here.

What I want exactly:

My idea is to create a method in my generic repository, this method will receive the entity that I want to select data from, and receive an expression that told it to load data from the scent entity and some related tables (not all related table but some related table that I will send in the expression).

How can I resolve this?

Answer

It is highly dependent on your specific implementation of generic repository, but the following code might help you to get the idea –

public async Task<TEntity> LoadSingleWithRelatedAsync<TEntity>(TEntity entity, params Expression<Func<TEntity, object>>[] expressionList) where TEntity : EntityBase
{
    if (entity == null)
        return null;

    var query = _DbCtx.Set<TEntity>().AsQueryable();
    foreach (var expression in expressionList)
    {
        query = query.Include(expression);
    }

    return await query.FirstOrDefaultAsync(p => p.Id == entity.Id);
}

where EntityBase is –

public class EntityBase
{
    public int Id { get; set; }
}

Following is an example of how you would use it –

// Order has a Customer parent and a list of OrderLine children
var loadedOrder = await _Repo.LoadSingleWithRelatedAsync(order, p => p.Customer, p => OrderLines);
  1. Order must be a class derived from EntityBase
  2. order can be Detached entity (e.g. received through a method parameter from client side), or an already existing entity fetched from database

EDIT: 2021.04.01
If you want a collection instead of a single entity, the method might look like –

public async Task<IEnumerable<TEntity>> LoadAllWithRelatedAsync<TEntity>(params Expression<Func<TEntity, object>>[] expressionList) where TEntity : class
{
    var query = _DbCtx.Set<TEntity>().AsQueryable();
    foreach (var expression in expressionList)
    {
        query = query.Include(expression);
    }

    return await query.ToListAsync();
}

Here –

  1. TEntity is not required to have the constraint of being an EntityBase type, since we don’t have any use of the Id property
  2. The parameter entity is not needed, since we are not looking for a specific entity
  3. To identity the type in the calling code, the type parameter must be used when calling the method –
// Order has a Customer parent and a list of OrderLine children
var orderList = await _Repo.LoadAllWithRelatedAsync<Order>(p => p.Customer, p => OrderLines);