I am creating an advanced search that converts an OData expression into a .NET expression tree (Expression<Func<T, bool>>
). I pass this expression into my EF6 .Select() method as a predicate and it works as expected.
However, in implementing this feature, I've discovered that LINQ methods only work with IQueryable<TSource>
. This works for .Set<T>()
, but I won't know the type at runtime, so I need to use .Set()
.
I could probably use reflection to call .Set<T>()
and then invoke it, but that seems like a bit of a hack, so I'd rather do it directly through .Set()
if at all possible.
If I understand correctly, you have LambdaExpression
instead of Expression<Func<T, bool>>
and you want to use it as Where
but on IQueryable
(which DbSet
class implements) rather than IQueryable<T>
.
All you need to know is that IQueryable<T>
extensions methods simply emit MethodCallExpression
to the corresponding Queryable
method in the query expression tree.
For instance, to emulate Where
or Select
on IQueryable
you can use the following custom extension methods:
public static class QueryableExtensions
{
public static IQueryable Where(this IQueryable source, LambdaExpression predicate)
{
var expression = Expression.Call(
typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery(expression);
}
public static IQueryable Select(this IQueryable source, LambdaExpression selector)
{
var expression = Expression.Call(
typeof(Queryable), "Select",
new Type[] { source.ElementType, selector.Body.Type },
source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery(expression);
}
}
You can do similar for other Queryable
methods needed.
Update: Since you are interesting in, here is an example of using expression prototypes to get a generic method definition and construct generic method from it:
public static class QueryableExtensions
{
static MethodInfo QueryableMethod<T>(this Expression<Func<IQueryable<object>, T>> prototype, params Type[] types)
{
return ((MethodCallExpression)prototype.Body).Method
.GetGenericMethodDefinition()
.MakeGenericMethod(types);
}
public static IQueryable Where(this IQueryable source, LambdaExpression predicate)
{
var expression = Expression.Call(
QueryableMethod(q => q.Where(x => true), source.ElementType),
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery(expression);
}
public static IQueryable Select(this IQueryable source, LambdaExpression selector)
{
var expression = Expression.Call(
QueryableMethod(q => q.Select(x => 1), source.ElementType, selector.Body.Type),
source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery(expression);
}
}