EF Core 3 not loading related data in second or deeper level

c# entity-framework entity-framework-6 entity-framework-core

Question

It works as anticipated with EF6 but I'm having trouble loading data from a second level object using EF Core 3. I'm converting a project to attempt to move everything to.NET Core, including EF.

I have a list of companies with users, and these users have roles. For some reason, I can load the companies and their relevant users from a many-to-many relationship, but the roles for each user are not being loaded. I'm not sure if this is the best way to put it, but in short, I have a list of companies with users, and these users have roles.

What I have is thisGeneric function:

protected virtual IQueryable<TEntity> GetQueryable(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = null,
    bool isCollection = false,
    int? skip = null,
    int? take = null)
{
    IQueryable<TEntity> query = this.Context.Set<TEntity>();

    if (filter != null)
    {
        query = query.Where(filter.Expand());
    }

    if (includeProperties != null)
    {
        query = includeProperties
            .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
            .Aggregate(query, (current, include) => current.Include(include));
    }

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

    if (skip.HasValue)
    {
        query = query.Skip(skip.Value);
    }

    if (take.HasValue)
    {
        query = query.Take(take.Value);
    }

    query = query.AsExpandableEFCore();

    return query;
}

I also refer to it as follows:

var databaseCompanies = await this.UnitOfWork.Companies
       .GetAllAsync(null, "Companies.User.Roles");

And these are the definitions of my entities:

Company:

public class Company: Entity<Guid>
{
    private ICollection<CompanyUsers> _companyUsers;

    public virtual ICollection<CompanyUsers> CompanyUsers
    {
        get => this._companyUsers ?? (this._companyUsers = new HashSet<CompanyUsers>());
        set => this._companyUsers = value;
    }
}

User:

public class User : Entity<Guid>
{
    private ICollection<CompanyUsers> _companyUsers;
    private ICollection<UserRole> _roles;

    public virtual ICollection<UserRole> Roles
    {
        get => this._roles ?? (this._roles = new HashSet<UserRole>());
        set => this._roles = value;
    }

    public virtual ICollection<CompanyUsers> CompanyUsers
    {
        get => this._companyUsers ?? (this._companyUsers = new HashSet<CompanyUsers>());
        set => this._companyUsers = value;
    }
}

Role:

public class UserRole
{
    public Guid UserId { get; set; }

    public RoleType Role { get; set; }

    public User User { get; set; }
}

Class to define the many to many relation:

public class CompanyUsers
{
    public Guid UserId { get; set; }

    public User User { get; set; }

    public Guid CompanyId { get; set; }

    public Company Company { get; set; }
}

As you can see, there is a one-to-many link between the User and UserRole classes and a many-to-many relationship between the Company and User classes as described by CompanyUsers. If you call:

var databaseCompanies = await this.UnitOfWork.Companies
       .GetAllAsync(null, "Companies.User");

It only loads the companies and the users, so I tried this instead:

var databaseCompanies = await this.UnitOfWork.Companies
       .GetAllAsync(null, "Companies.User.Roles");

or

var databaseCompanies = await this.UnitOfWork.Companies
      .GetAllAsync(null, "Companies.User,Companies.User.Roles");

However, none of them function, and the company-specific user roles are never loaded.

The last example will add 2 to the total. Include but I realized that the expression contains two parameters, but they are of different types when I looked at the query variable:

One value is value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable'1[Data.Entities.Company]). Secondly, "Company.User.Roles"

Once again, if I load a particular user or another entity that is just one level below the main entity, the responsibilities will be loaded as appropriate:

var databaseUser = await this.UnitOfWork.Users
      .GetFirstAsync(u => u.Username == username, null, "Roles", true);

Any suggestions on how to fix this?

Thanks.

UPDATE:

I truly wish I could swear right now, like I indicated below! It dawned on me that the EF Core-generated query was accurate and had correctly returned the roles as part of the query, but they were all NULL.

I went to check my Roles table and that's when I realized that it had been wiped when I rolled back a migration regarding roles in order to fix the relationship and I never re-generated the dummy roles that were associated with all the users of the companies I had defined!!

I apologize to everyone who assisted!

1
0
12/27/2019 11:42:28 PM

Popular Answer

As youGetQueryable replaces method parametersstring includeProperties = null with Func<IQueryable<T>, IIncludableQueryable<T, object>> includes == null then edit the code as follows in the method body:

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

Now when calling theGetQueryable pass the value of the methodincludes the following

sp => sp.Include(i => i.FirstLevel).ThenInclude(f => f.SecondLevel)

Good work! Now it ought to function as planned!

0
12/27/2019 4:41:00 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow