Adurahman
Adurahman

Reputation: 17

Multiple .Include in Repository

At first, here what I have:

4 Classes have some relations between them as:

Project has many Bridges.

Bridge has many Foundations.

Foundation has many PierCaps and many PierColumns.

Project.cs:

public class Project
{
    public Project()
    {
        Bridges = new HashSet<Bridge>();

        StartDate = DateTime.Now;
        EndDate = DateTime.Now;
    }

    // Fields
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }

    // Calculated Fields
    public int BridgeCount => Bridges.Count;
    public int PierFoundationCount = 0;
    public int PierCapCount = 0;
    public int PierColumnCount = 0;

    // Relation To Children
    public virtual ICollection<Bridge> Bridges { get; private set; }
}

Bridge.cs:

public class Bridge : BasicBaseEntityWithName
{
    public Bridge()
    {
        PierFoundations = new HashSet<PierFoundation>();
    }

    // Fields
    public int ProjectID { set; get; }
    public Project Project { set; get; }

    // Calculated Fields
    public int PierFoundationCount => PierFoundations.Count;
    public int PierCapCount => PierFoundations.Count;
    public int PierColumnCount => PierFoundations.Count;

    // Relation To Children
    public ICollection<PierFoundation> PierFoundations { get; private set; }
}

PierFoundation.cs:

public class PierFoundation : BasicBaseEntityWithName
{
    public PierFoundation()
    {
        PierCaps = new HashSet<PierCap>();
        PierColumns = new HashSet<PierColumn>();
    }

    // Fields
    public float Length { set; get; }

    // Calculated Fields
    public int PierColumnCount => PierColumns.Count;
    public int PierCapCount => PierCaps.Count;
    public float FoundationQty => (Length * Width * Height);

    // Relations To Parents
    public int BridgeID { set; get; }
    public Bridge Bridge { set; get; }

    // Relations To Children
    public ICollection<PierCap> PierCaps { get; set; }
    public ICollection<PierColumn> PierColumns { get; set; }
}

PierColumn.cs:

public class PierColumn : BaseEntityNoName
{
    // Fields
    public float Diameter { set; get; }

    // Relations To Parents
    public int PierFoundationID { set; get; }
    public PierFoundation PierFoundation { set; get; }
}

PierCap.cs:

public class PierCap : BaseEntityNoName
{
    // Fields
    public float CapWidth { set; get; }

    // Relations To Parents
    public int PierFoundationID { set; get; }
    public PierFoundation PierFoundation { set; get; }
}

BaseEntityNoName.cs:

public class BaseEntityNoName
{
    public BaseEntityNoName()
    {
        AddedBy = Core.General.Global.CurrentUseId;
        AddedDate = DateTime.Now;
    }
    public int Id { get; set; }

    public DateTime AddedDate { get; set; }
    public int AddedBy { get; set; }

    // Becuase they will not appear at new, they will be nullable
    public DateTime? ModifiedDate { get; set; }
    public int? ModifiedBy { get; set; }
}

BasicBaseEntityWithName.cs:

public class BasicBaseEntityWithName : BaseEntityNoName
{
    public string Name { get; set; }
}

Adding to that, I have this method in my Repository:

public IEnumerable<TEntity> Include(params Expression<Func<TEntity, object>>[] includeExpressions)
{
    DbSet<TEntity> dbSet = Context.Set<TEntity>();

    IEnumerable<TEntity> query = null;
    foreach (var includeExpression in includeExpressions)
    {
        query = dbSet.Include(includeExpression);
    }

    return query ?? dbSet;
}

My questions are:

Q1. When I use Include the related entities (PierFoundation, PierCap, PierColumn), I don't get them all!. So, when I type:

    PierFoundations = CurrentUnitOfWork.PierFoundationRepository.Include(x => x.PierColumns, x => x.PierCaps);

I received just PierFoundation and PierCap, but not PierColumn

What is the wrong here?. I want to retrieve all data in the all three of them.

Q2. In the Project class, I have four calculate fields:

The last three, I don't know how to get them, because they don't have direct relation with Project

BTW: I am using EF Core 2

Upvotes: 1

Views: 1233

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205629

The two issues are unrelated, so answering only the main (as indicated by the post title) Q1:

When I use Include the related entities (PierFoundation, PierCap, PierColumn), I don't get them all!.

It's caused by the following line in your repository method implementation:

query = dbSet.Include(includeExpression);

As with many LINQ method, it's important to use the Include method return value when chaining with other calls, and as you can see, this code stores only the last Include call, hence the behavior you are observing.

It can be fixed by changing the implementation like this:

var query = Context.Set<TEntity>().AsQueryable();
foreach (var includeExpression in includeExpressions)
    query = query.Include(includeExpression); // Note the usage of query variable
return query;

or simply

return includeExpresions.Aggregate(Context.Set<T>().AsQueryable(), (q, e) => q.Include(e)); 

But note that EF Core uses different approach for including nested data levels (ThenInclude) which cannot be expressed with Expression<Func<T, object>>, so you might consider exposing IQueryable<TEntity> returning method from your repository and use the EF Core provided Include / ThenInclude extension methods.

What about Q2, consider posting it in a separate SO question (post).

Upvotes: 3

Related Questions