繁体   English   中英

转换表达式 <Func<T,T,bool> &gt;表达 <Func<T,bool> &gt;

[英]Convert Expression<Func<T,T,bool>> to Expression<Func<T,bool>>

我有这样的表达方式

(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)=> a.Id == b.IdId = 5的实体实例可以得到一个新的表达式(a)= > a.Id == 5
LINQ to Entities的最后一个表达式会很好。

我发现并阅读了这篇文章
替换lambda表达式中的参数
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
但仍然不知道如何解决我的任务

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM