转换Expression< Func< T, T, bool>比;Expression< Func< T, bool>祝
本文关键字:Func bool Expression 转换 | 更新日期: 2023-09-27 18:17:19
我有一个这样的表达式
(a,b) => a.Id == b.Id
我想使用它在LINQ实体查询
T GetSingle(IRepository<T> repository, Func<T,T,bool> predicate, T entity)
{
return repository.GetAll().Single(e => predicate(e, entity))
}
但这会导致异常:LINQ表达式节点类型'Invoke'不支持LINQ to Entities
据我所知,我可以使用表达式为LINQ2SQL构造一个有效的谓词,所以我的表达式(a,b) => a.Id == b.Id和Id = 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>
),并创建一个包装器本身调用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());
}