转换表达式<func <t,t,bool>&gt;表达式<func <t,bool>&gt; </func <t,bool> </func <t,t,bool>

时间:2013-06-07 13:26:23

标签: c# linq-to-entities

我有一个像这样的表达

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

我想在LINQ to Entities查询中使用它

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

但是这会导致异常:LINQ to Entities中不支持LINQ表达式节点类型'Invoke' 据我所知,我可以使用表达式为LINQ2SQL构造一个valide谓词,所以我的表达式 (a,b)=&gt; a.Id == b.Id Id = 5 的实体实例可以产生新的表达式(a)=&gt; a.Id == 5
LINQ to Entities的最后一个表达式将很好。

我发现并阅读了这篇文章 Replace parameter in lambda expression
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
但仍然不知道如何解决我的任务

那么,如何动态转换给定的表达式?

2 个答案:

答案 0 :(得分:3)

为什么不把你的方法改为:

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

所以不要这样:

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

你应该能够做到这一点:

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

答案 1 :(得分:1)

我还没有用Linq2SQL测试它,但在我看来你应该能够用表达式访问者执行此操作并编译表达式以将参数的值写入您提供的表达式中(假设您切换到使用Expression<Func<T, T, bool>>而不是Func<T, T, bool>)并创建一个包装器,它自己在Enumerable.Single

的结果上调用GetAll

访客(特别是你给出的例子看起来像这样)

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;
    }
}

现在,我们将GetSingle方法调整为:

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()); 
}

当然,诀窍就是让它表现良好。