2

我有这样的表情

(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 表达式节点类型“调用”
据我了解,我可以使用表达式为 LINQ2SQL 构造一个有效谓词,因此我的表达式 (a,b) => a.Id == b.IdId = 5的实体实例可以产生一个新的表达式(a) => a.Id == 5
最后一个表达式适用于 LINQ to Entities。

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

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

4

2 回答 2

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);
于 2013-06-07T13:44:10.703 回答
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()); 
}

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

于 2013-06-07T14:28:09.847 回答