'Entity Framework .Include() with condition not working

SCENARIO

If I split the Entity Framework linq query to evaluate an IQueryable first and after try to include some properties, the value of AccessGroupAccessPoints and AccessGroupAccessPoints.AccessPoint will not be included in the end.

    public Task<List<AccessGroup>> GetAll(bool includeRelations)
    {
        var q = context.Set<AccessGroup>().AsNoTracking();

        if (includeRelations)
        {
            q.Include(a => a.AccessGroupAccessPoints)
                .ThenInclude(a => a.AccessPoint);
        }
        
        return q.ToListAsync();
    }

I also tried to remove .ThenInclude(), use string as parameter of .Include(), move .AsNoTracking() to resolve after... Neither of these options worked.

    public Task<List<AccessGroup>> GetAll(bool includeRelations)
    {
        var q = context.Set<AccessGroup>();

        if (includeRelations)
        {
            q.Include("AccessGroupAccessPoints");
        }

        return q.AsNoTracking().ToListAsync();
    }

SOLUTION

But I found out a solution, if don't split the query until it evaluates the object List, Entity Framework includes the expected properties. I hope it help.

    public Task<List<AccessGroup>> GetAll(bool includeRelations)
    {
        if (includeRelations)
        {
            return context.Set<AccessGroup>()
                .Include(a => a.AccessGroupAccessPoints).ThenInclude(a => a.AccessPoint)
                .AsNoTracking()
                .ToListAsync();
        }
        else
        {
            return context.Set<AccessGroup>().AsNoTracking().ToListAsync();
        }
    }
    

QUESTIONS

Why does this happen?

Is this an Entity Framework bug?



Solution 1:[1]

For a similar reason that this doesn't sort a list in place:

var x = new List<int> { 3, 2, 1 };

x.OrderBy(i => i);

Console.WriteLine(string.Join("|", x));    //prints 3,2,1

You need to capture and use the result of the OrderBy for it to have an effect:

var x = new List<int> { 3, 2, 1 };

var y = x.OrderBy(i => i);

Console.WriteLine(string.Join("|", y));    //prints 1,2,3

In your first code, you make q, then you call Include() on it, but don't use the return value from Include, and Include doesn't modify q so that it becomes something that does an include (just like calling OrderBy on a list doesn't jiggle its items so they are in order) so the knowledge of why you included is lost..

..yet, in your second code you use the return value from Include when you run the query (because of the chain)


Perhaps you need something like:

public Task<List<AccessGroup>> GetAll(bool includeRelations)
{
    var q = context.AccessGroups.AsQueryable();    //so we can assign the IIncludableQueryable generated by Include.ThenInclude back to q

    if (includeRelations)
    {
        q = q.Include(a => a.AccessGroupAccessPoints)
            .ThenInclude(a => a.AccessPoint);
    }
    
    return q.ToListAsync();
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1