LINQKit
LINQKit copied to clipboard
NotSupportedException using ExpandableQuery as nested query
I have a number of library functions (QueryObjectAccess in this case) that perform filtering on a IQueryable, often involving PredicateBuilder. I don't want to forget to apply AsExpandable(), so I'm calling it inside these functions. Worst case up until now, AsExpandable() was a no-op that that returned the already expandable query.
I've recently added some features that use these filtered queries as criteria for other queries, for example:
productQuery = QueryObjectAccess(productQuery, user); // returns expandable query
var categoryQuery = _db.Categories.AsExpandable()
.Where(c => productQuery.Any(p => p.Categories.Any(pc => DbFunctions.Like(pc.Category.HierarchyKey, c.HierarchyKey + "%"))))
.Distinct();
This is causing a NotSupportedException: Unable to create a constant value of type 'Models.Product'. Only primitive types or enumeration types are supported in this context.
It seems like the ExpressionExpander could handle this, but doesn't appear to. Is there a better way to handle this?
If I change QueryObjectAccess() to call Expand on the expression instead, it works. Is that a better approach for something buried in a library?
(I realize that in case I could conditionally call AsExpandable on productQuery depending on how it was to be used, but I can't always do that)
Is there any news on this? LINQKit making it impossible to compose Queryables is a pretty significant shortcoming, and the resulting error message is almost untracably misleading.
This issue is also important for us, as we use LINQKit extensively in our project and there are a lot of shared methods returning expandable queries. Combining those queries in a single query via join, contains, etc. is currently very hard or impossible due to that issue.
From what I found so far, the problem happens only in EF6. At some point of building a query it tries to retrieve ObjectQuery instance for another subtree and this where it hits ExpandableQuery.
Since it's neither an instance of ObjectQuery nor it implements IInternalQueryAdapter, EF falls back to IEnumerable.
My guess is that the fix should be somewhere in the ExpressionVisitor where it would provide an instance of ExpandableQuery.InnerQuery instead of just ExpandableQuery. I'll continue looking into this and will hopefully provide a PR. Any hints and suggestions about where exactly the fix should be done are very welcome.
Here's also a simple test case that can be added in DbAsyncTests.cs to reproduce the issue:
[Fact]
public async Task DbAsync_ExpandNestedQueries()
{
Expression<Func<Entity, int>> getEntityValue = e => e.Value;
Expression<Func<RelatedEntity, int>> getRelatedEntityValue = e => e.Value;
var query1 = _db.Entities.AsExpandable().Select(e => getEntityValue.Invoke(e));
var query2 = _db.RelatedEntities.AsExpandable().Select(e => getRelatedEntityValue.Invoke(e));
var query =
from q1 in query1
from q2 in query2
select new { q1, q2 };
await query.ToListAsync();
}
Please share your ideas for a workaround if any.