Entity Framework Core “AsQueryable” Issue

Home / Entity Framework Core “AsQueryable” Issue

Earlier, I was dinking around with some base code I wrote for EF 6.x and recently ported over to EF Core. It’s a nice bit of code that can build LINQ expressions for nearly any search/query scenario regardless of the domain models behind the scenes. It utilizes the LINQ Expression libraries and some reflection. The code works fine in EF 6.x, but I ran into a weird issue when building expressions for many-to-many relationships.

For the many to many relationship, I got this exception:

NotSupportedException: Could not parse expression ‘param.ChildList.AsQueryable()’: This overload of the method ‘System.Linq.Queryable.AsQueryable’ is currently not supported.

I scratched my head initially and wondered why this didn’t work in EF Core like it does in EF 6.x.

The premise of the code is that it looks at the Property being queried, determines that it’s a List navigation property, and generates the appropriate Where/Count/AsQueryable execution path.

The offending code is this:

//This is the old code that casts to Queryable .. it doesn't appear to work in EF Core.
var whereMethod = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(m => m.Name == "Where").MakeGenericMethod(childType);
var countMethod = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(m => m.Name == "Count" && m.GetParameters().Length == 1).MakeGenericMethod(childType);
var asQueryableMethod = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault(m => m.Name == "AsQueryable").MakeGenericMethod(childType);

// Child expression
var expression = (Expression)typeof(ExpressionBuilder).GetMethod("GetExpression").MakeGenericMethod(childType).Invoke(null, new object[] { childSearch, false });

// Convert the List<TChild> to IQueryable<TChild>
var convExp = Expression.Call(asQueryableMethod, listPropExp);

// Build the where expression based on where method and our generated child expression (based on TChild)
Expression whereCall = Expression.Call(whereMethod, convExp, expression);

// We want the expression to evaluate to true where the child count is > 0
Expression target = Expression.Constant(0, typeof(int));
var method = Expression.GreaterThan(Expression.Call(countMethod, whereCall), target);
var retExp = Expression.Lambda<Func<T, bool>>(method, parameter);
return retExp;

The “expression” line above basically gets an expression for the child navigation property’s property to be searched. Suffice it to say, without posting a ton of code, it’s a parameter expression.

To fix the exception, fortunately, it was pretty easy to build up an IQueryable.Any(x => x.Prop == ‘whatever’) manually and see if it worked in EF Core. It does work, so going a bit further, it was clear to see in debugging the expression that the a “.Call System.Linq.Enumerable.Any” was generated. Using this bit of knowledge, the generically generated LINQ expression/binary tree becomes quite simpler to produce.

We can grab a handle to the Enumerable “Any” method and invoke it in the same manner as the previous code.

// New EF Core compatible
var anyMethod = typeof(Enumerable)
    .GetMethods(BindingFlags.Static | BindingFlags.Public)
    .FirstOrDefault(m => m.Name == "Any" && m.GetParameters().Count() == 2)

// Child expression
var expression = (Expression)typeof(ExpressionBuilder).GetMethod("GetExpression").MakeGenericMethod(childType).Invoke(null, new object[] { childSearch, false });

// We want the expression to evaluate to true where the child count is > 0
var method = Expression.Call(anyMethod, listPropExp, expression);
var retExp = Expression.Lambda<Func<T, bool>>(method, parameter);
return retExp;

After that change, everything works and everyone is happy again. In hindsight, I probably should have been invoking/calling the “Any” method to begin with, but went the Where/Count route long ago and hadn’t had issues.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.