Home > Mobile >  Entity Framework "Self-casting Include" trick no longer works in .NET 5/6
Entity Framework "Self-casting Include" trick no longer works in .NET 5/6

Time:11-15

I was very happy the day I discovered this Entity Framework Core 3.1 trick:

private static IIncludableQueryable<Widget, Widget> WidgetQueryIncludeFringles(IQueryable<Widget> widQuery)
{
    return widQuery
               .Include(w => w.Fringles).ThenInclude(f => f.Bauble)
               .Include(w => w);
}

Why? Because Widget has a bunch of navigation properties and I want to ‘include’ any combination of them, depending on the situation. By having composable queries like the above I avoid a combinatorial explosion.

But each of these methods must have that common return type. That “w => w” trick is a lifesaver; I can’t find any other way of converting the query to the required return type. Life is good, until I try to convert from EF 3.1 to EF 6.0.

Now I get this exception at runtime:

System.InvalidOperationException: 'The expression 'w' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'

I know that EF 5 has new features for filtering the Includes and I'm guessing that the exception is a consequence of implementing these features. Is there some way I can tweak the above method (without changing the signature) so that it will both compile and run under EF 5/6?

CodePudding user response:

The whole purpose of the second generic type argument of IIncludableQueryable is to allow chaining ThenInclude. Thus IIncludableQueryable<T, T> makes no sense, as the only thing it would allow (if supported) would be ThenInclude(T x => x.{Something}).

But note that IIncludableQueryable<T, P> is inheriting (i.e.is a) IQueryable<T>, which is sufficient to continue chaining other Include for the initial entity type T (the "path" is reset to the beginning).

So removing the unnecessary (and now unsupported) trick and changing the return type allows you to achieve the same goal, e.g.

private static IQueryable<Widget> WidgetQueryIncludeFringles(IQueryable<Widget> widQuery)
{
    return widQuery
        .Include(w => w.Fringles).ThenInclude(f => f.Bauble);
}

and then

IQueryable<Widget> query = ...;
query = WidgetQueryIncludeFringles(query)
    .Include(w => w.SecondNavigation)
    .Include(w => w.ThirdNavigation);
  • Related