我正在尝试创建一个动态AndAlso
过滤器,该过滤器将在Where
方法中用于LINQ-to-EF
查询:
query.Where(filterExpression)
其中filterExpression
是已编译的lambda
到目前为止,我已经实现了一个带有一些奇特用法的循环来检查只有一个,两个或更多查询的情况。
Expression selectLeft = null;
Expression selectRight = null;
Expression filterExpression = null;
foreach (QueryColumn columnQuery in querycolumns)
{
Expression<Func<FileColumnRecords, bool>>
columnPredicate = d => d.fcv.Any(f => (f.value != null ?
f.value.ToLower().Contains(columnQuery.queryTerm.ToLower()) :
false));
if (selectLeft == null)
{
selectLeft = columnPredicate.Body;
filterExpression = selectLeft;
continue;
}
if (selectRight == null)
{
selectRight = columnPredicate.Body;
filterExpression =
Expression.AndAlso(selectLeft, selectRight);
continue;
}
filterExpression =
Expression.AndAlso(filterExpression, columnPredicate.Body);
}
然后我设置ParameterReplacer
以确保我的表达式参数的所有迭代都得到相同的引用:
ParameterExpression param = Expression.Parameter(typeof(FileColumnRecords), "p");
ParameterReplacer replacer = new ParameterReplacer(param);
filterExpression = replacer.Visit(filterExpression);
建立于:
class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression parameter;
internal ParameterReplacer(ParameterExpression parameter)
{
this.parameter = parameter;
}
protected override Expression VisitParameter
(ParameterExpression node)
{
return parameter;
}
}
(ParameterReplacer
课程由JonSkeet提供)
运行ParameterReplacer
时出现以下错误:
"Property 'System.String value' is not defined for type 'Models.FileColumnRecords'"
其中FileColumnRecords
定义为:
public class FileColumnRecords
{
public Documents doc;
public IEnumerable<FileColumnValues> fcv;
}
和FileColumnValues
as:
public partial class FileColumnValues
{
public long ID { get; set; }
public long CNID { get; set; }
public long fileID { get; set; }
public string value { get; set; }
}
答案 0 :(得分:3)
我怀疑,由于表达式树中嵌入了参数,您遇到了困难。替换者应该只替换顶级参数。
您可以将顶级表达式传递给ParameterReplacer
:
class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression target;
private readonly ISet<ParameterExpression> sources;
internal ParameterReplacer(ParameterExpression target,
IEnumerable<LambdaExpression> expressions)
{
this.target = target;
sources = new HashSet<ParameterExpression>(
expressions.SelectMany(e => e.Parameters));
}
protected override Expression VisitParameter
(ParameterExpression node)
{
return sources.Contains(node) ? target : node;
}
}
然后,您需要更改代码的其余部分,以构建您已合并的表达式树列表。肯定有更优雅的方式,但我认为这应该有效。但是,这只是我的头脑 - 我还没有尝试过。
说完这一切之后,您确定不能只使用PredicateBuilder
吗?
答案 1 :(得分:0)
根据JonSkeet Answer的帖子,使用了PredicateBuilder
JonSkeet在LINQKit库中提出的一个惊人的方法{{3}},我想出了以下简单的解决方案:
public static IQueryable<FileColumnRecords> DynamicColumnQuery
(this IQueryable<FileColumnRecords> query,
List<QueryColumn> querycolumns)
{
var predicate = PredicateBuilder.False<FileColumnRecords>();
foreach (QueryColumn columnQuery in querycolumns)
{
string temp = columnQuery.queryTerm.ToLower();
predicate = predicate.Or(d => d.fcv.Any(f => (f.value != null ?
f.value.ToLower().Contains(temp) : false));
}
return query.AsExpandable().Where(predicate);
}
如果你对编写表达式树有一点了解,那么你就会立刻意识到这是多么令人难以置信的 AWESOME 。另外,请注意我在谓词中嵌入参数的方式。
我与LINQKit的开发者,Joseph或Ben Albahari没有任何关系 也不是O&Reilly出版商。只是一个非常满意的开发人员认为我应该 分享他们所建造的精彩内容。