FireAlkazar
FireAlkazar

Reputation: 1882

Convert Expression<Func<T,T,bool>> to Expression<Func<T,bool>>

I have an expression like this

(a,b) => a.Id == b.Id

I would like to use it in LINQ to Entities query

T GetSingle(IRepository<T> repository, Func<T,T,bool> predicate, T entity)
{
   return repository.GetAll().Single(e => predicate(e, entity))
}

but this results the exception: LINQ expression node type 'Invoke' is not supported in LINQ to Entities
As I understand I can use Expressions to construct a valide predicate for LINQ2SQL, so my expression (a,b) => a.Id == b.Id and instance of entity with Id = 5 can result a new expression (a) => a.Id == 5.
And the last expression will be fine for LINQ to Entities.

I found and read this articles
Replace parameter in lambda expression
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
but still has no clue how to solve my task

So, how do I convert given expression dynamically?

Upvotes: 2

Views: 2130

Answers (2)

mlorbetske
mlorbetske

Reputation: 5649

I haven't tested it with Linq2SQL, but it seems to me that you should be able to do this with an expression visitor and compiling the expression to write the value of your parameter into the expression you've supplied (assuming you switch over to using Expression<Func<T, T, bool>> instead of Func<T, T, bool>) and creating a wrapper that itself invokes Enumerable.Single on the result from the GetAll

The visitor (for specifically the example you've given would look like this)

public class VariableSubstitutionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _parameter;
    private readonly ConstantExpression _constant;

    public VariableSubstitutionVisitor(ParameterExpression parameter, ConstantExpression constant)
    {
        _parameter = parameter;
        _constant = constant;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == _parameter)
        {
            return _constant;
        }

        return node;
    }
}

Now, we'd adjust the GetSingle method to look like this:

public T GetSingle(IRepository<T> repository, Expression<Func<T, T, bool>> predicate, T entity)
{
    //Create a new representation of predicate that will take just one parameter and capture entity

    //Get just the body of the supplied expression
    var body = predicate.Body;
    //Make a new visitor to replace the second parameter with the supplied value
    var substitutionVisitor = new VariableSubstitutionVisitor(predicate.Parameters[1], Expression.Constant(entity, typeof(T)));
    //Create an expression that represents the predicate with the second parameter replaced with the supplied entity
    var visitedBody = substitutionVisitor.Visit(body).Reduce();
    //Make the new expression into something that could be a Func<T, bool>
    var newBody = Expression.Lambda<Func<T, bool>>(visitedBody, predicate.Parameters[0]); 

    //Now, create something that will call Enumerable.Single on the result of GetAll from the repository, supplying the new predicate

    //Make a place to hold the result of GetAll
    var resultsParameter = Expression.Parameter(typeof (IEnumerable<T>));
    //Make an expression that calls the Single extension method
    var singleExpression = Expression.Call(((Func<IEnumerable<T>, Func<T, bool>, T>)Enumerable.Single).Method, resultsParameter, newBody);
    //Make a Func<IEnumerable<T>, T> that return the result of the call of Single on the results of the GetAll method
    var compiled = Expression.Lambda<Func<IEnumerable<T>, T>>(singleExpression, resultsParameter).Compile();
    //Call GetAll, letting the new method that we've got run the supplied predicate without having to run an Invoke type expression
    return compiled(repository.GetAll()); 
}

The trick, of course, is getting that to perform well.

Upvotes: 1

Eren Ers&#246;nmez
Eren Ers&#246;nmez

Reputation: 39085

Why don't you just change your method to be:

T GetSingle(IRepository<T> repository, Expression<Func<TSource, Boolean>> predicate)
{
   return repository.GetAll().Single(predicate);
}

so instead of this:

GetSingle(myRepository, (a,b) => a.Id == b.Id, myEntity);

you should be able to do this:

GetSingle(myRepository, a => a.Id == myEntity.Id);

Upvotes: 3

Related Questions