tags:

views:

81

answers:

3

I have expression

Expression<Func<Car, Driver, bool>> CanBeDrivenBy = 
    (car, driver) => car.Category == 'B' && driver.Age > 18;

and I want to get cars which can be driven by some driver

IQueryable<Cars> cars = ...;
Driver driver = ...;
cars.Where(CanBeDrivenBy);   // Fail, expecting Expression<Func<Car, bool>>

So I need to convert Expression<Func<Car, Driver, bool>> to Expression<Func<Car, bool>> (specify driver)

Yes I can use

cars.Where(c => c.Category == 'B' && driver.Age > 18);

but I need solution with expression which can be changed dynamicly. And I need to pass Expression (using entity framework)

A: 
cars.Where(c => CanBeDrivenBy(c, driver))
svick
Nope, not working. Expression cannot be called like function. And I don't want to compile expression (Entity framework creates SQL query from expression to be evaluated in database)
Blackfighter
+2  A: 

This WORKS

I wrote this function to reduce number of arguments from 2 to 1 by specifing second argument.

public static Expression<Func<T1, TResult>> Bind2nd<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> source, T2 argument)
{
        Expression arg2 = Expression.Constant(argument, typeof(T2));
        var arg1 = Expression.Parameter(typeof(T1));
        return Expression.Lambda<Func<T1, TResult>>(Expression.Invoke(source, arg1, arg2), arg1);
}

and usage

IQueryable<Cars> cars = ...;
Driver driver = ...;
cars.Where(Bind2nd(CanBeDrivenBy, driver));

arg1 is temporal storage between calls.

Is there any system equivalent function?

Blackfighter
You might want to mention that this is called currying.
leppie
Oh, oops, this is your question too :) Anyways, that's how I would do it. You could probably take it further, and use only the body of the lambda expressions.
leppie
A: 

you can reuse modified version of source expressions body

using System;
using System.Linq.Expressions;

public class Program
{
    public static Expression<Func<T1, TResult>> Bind2nd<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> source, T2 argument)
    {
        Expression arg2 = Expression.Constant(argument, typeof (T2));
        Expression newBody = new Rewriter(source.Parameters[1], arg2).Visit(source.Body);
        return Expression.Lambda<Func<T1, TResult>>(newBody, source.Parameters[0]);
    }

    public static void Main(string[] args)
    {
        Expression<Func<string, string, int>> f = (a, b) => a.Length + b.Length;
        Console.WriteLine(f); // (a, b) => (a.Length + b.Length)

        Console.WriteLine(Bind2nd(f, "1")); // a => (a.Length + "1".Length)
    }

    #region Nested type: Rewriter

    private class Rewriter : ExpressionVisitor
    {
        private readonly Expression candidate_;
        private readonly Expression replacement_;

        public Rewriter(Expression candidate, Expression replacement)
        {
            candidate_ = candidate;
            replacement_ = replacement;
        }

        public override Expression Visit(Expression node)
        {
            return node == candidate_ ? replacement_ : base.Visit(node);
        }
    }

    #endregion
}
desco
You might want to mention that your example code is .NET 4 specific.
leppie
the only part that is bound to 4.0 is ExpressionVisitor. Its implementation is pretty simple so if necessary it can be reconstructed for lesser framework versions
desco