I need to append methods to an existion experssion and combine them into a new resultExpression.
Expression<TSource, IQueryable<TResult>> sourceExpression;
Expression<TSource, int, int, IQueryable<TResult>> resultExpression;
I need to append Queryable.Skip() and Queryable.Take() methods to sourceExpression and convert altogether to a resultExpression. How can I do it using c# Expression methods?
I tried to use Expression.Lambda<Func<TSource, int, int, IQueryable>> with Expression.Call, but it throws InvalidOperationException when I pass Queryable methods to Expression.Call parameters
var skipCall = Expression.Call(
typeof(Queryable),
nameof(Queryable.Skip),
new[] {typeof(TResult)},
sourceExpression.Body,
Expression.Parameter(typeof(int))
);
var takeCall = Expression.Call(
typeof(Queryable),
nameof(Queryable.Take),
new[] {typeof(TResult)},
skipCall,
Expression.Parameter(typeof(int))
);
var resultExpression = Expression.Lambda<Func<TSource, int, int, IQueryable<TResult>>>(
takeCall, sourceExpression.Parameters
);
No generic method 'Take' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic
...
So I'm able to build final resultExpression using Expression.Call with Func needed from Queryable
var skipParameter = Expression.Parameter(typeof(int), "skip");
var skipFunc = new Func<IQueryable<TResult>, int, IQueryable<TResult>>(Queryable.Skip).Method;
var takeParameter = Expression.Parameter(typeof(int), "take");
var takeFunc = new Func<IQueryable<TResult>, int, IQueryable<TResult>>(Queryable.Take).Method;
var initialCall = Expression.Invoke(sourceExpression,sourceExpression.Parameters[0]);
var skipCall = Expression.Call(
skipFunc,
initialCall,
skipParameter);
var takeCall = Expression.Call(
takeFunc,
skipCall,
takeParameter);
resultExpression = Expression.Lambda<Func<TSource, int, int, IQueryable<TResult>>>(
takeCall,
sourceExpression.Parameters[0],
skipParameter,
takeParameter);
however I get Invoke() method in resulted expression string which can't be translated further. (source, skip, take) => Invoke(source => // sourceExpression...), source).Skip(skip).Take(take) How can I get rid of wrapping Invoke()?
So it turns out that I need to build lambda from original expression with Body as a parameter
// Skip() function from Queryable with 'skip' parameter
var skipParameter = Expression.Parameter(typeof(int), "skip");
var skipFunc = new Func<IQueryable<TResult>, int, IQueryable<TResult>>(Queryable.Skip).Method;
// Take() function from Queryable with 'take' parameter
var takeParameter = Expression.Parameter(typeof(int), "take");
var takeFunc = new Func<IQueryable<TResult>, int, IQueryable<TResult>>(Queryable.Take).Method;
// Create lambda expresion from source
var initialCall = Expression.Lambda<Func<TSource, IQueryable<TResult>>>(
sourceExpression.Body,
sourceExpression.Parameter[0]);
// Append Skip function
var skipCall = Expression.Call(
skipFunc,
initialCall.Body,
skipParameter);
// Append Take function
var takeCall = Expression.Call(
takeFunc,
skipCall,
takeParameter);
// Wrap calls in the final expression
resultExpression = Expression.Lambda<Func<TSource, int, int, IQueryable<TResult>>>(
takeCall,
contextParameter,
skipParameter,
takeParameter);
That finally gives me the result I need.