Search code examples
c#lambdaexpression-treesiqueryable

Build LambdaExpression with custom parameter selection


Complete definition of my extension method goes first.

static IQueryable<TResult> WithTicketNumbers<T, TResult>(
    this IQueryable<T> q,
    Expression<Func<T, Ticket>> ticketSelector,
    Expression<Func<T, string, TResult>> resultSelector,
    int digitsCount)

I have this IQueryable<T> q sequence, which is the target of the extension.

This method takes tickets from q selected by ticketSelector:

var tickets = q.Select(ticketSelector);

Next, and the main goal of the method, is taking some string-linq-supported-info from Ticket class and projecting both Ticket and string info next to the sequence:

var tickets2 = tickets.Select(W => new { Ticket = W, Info = W.Name + "123"});

Finally, I want my method to return an IQueryable which will select what user wants in resultSelector. This result selector picks both the ticket and the info parameters and produces what user wants it to produce. I am kind of stuck with the Expression class to create appropriate expression.

So far, I got my two parameters:

ParameterExpression tParameter = Expression.Parameter(typeof(T));
ParameterExpression stringParameter = Expression.Parameter(typeof(string));

Also, as I think, the final lambda:

Expression<Func<T, string, TResult>> lambda =
    Expression.Lambda<Func<T, string, TResult>>
    (/* ? */, tParameter, stringParameter);

However, I cannot figure out the body.

I can do Expression.Property to by reflection get the two properties Ticket and Info, but there is a type required, and I have anonymous type there, in tickets2.

Next, (as I guess) I need to use that lambda inside the tickets2 to produce the method result IQueryable<TResult>.

So how should I build that final expression?


Solution

  • Solved:

    /// <summary>
    /// Returns a queryable sequence of TResult elements which is composed through specified property evaluation.
    /// </summary>
    public static IQueryable<TResult> WithInfo<TItem, TProperty, TResult>(this IQueryable<TItem> q, Expression<Func<TItem, TProperty>> propertySelector, Expression<Func<TItem, TProperty, TResult>> resultSelector)
    {
        ParameterExpression param = Expression.Parameter(typeof(TItem));
        InvocationExpression prop = Expression.Invoke(propertySelector, param);
    
        var lambda = Expression.Lambda<Func<TItem, TResult>>(Expression.Invoke(resultSelector, param, prop), param);
        return q.Select(lambda);
    }