1

我正在尝试创建一个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作为:

public partial class FileColumnValues
{
    public long ID { get; set; }
    public long CNID { get; set; }
    public long fileID { get; set; }
    public string value { get; set; }
}
4

2 回答 2

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吗?

于 2014-09-08T15:23:38.163 回答
0

基于 JonSkeet 对这篇文章的回答PredicateBuilder,使用了JonSkeet 从LINQKit库中建议的惊人方法,我想出了以下简单的解决方案:

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

如果您对编写表达式树有一点了解,那么您将立即体会到这是多么令人难以置信。另外,请注意我是如何在我的谓词中嵌入参数的。

我绝不隶属于 LINQKit 的开发人员,也不隶属于 Joseph 或 Ben Albahari 或 O'Reilly 出版商。只是一个非常满意的开发人员,他认为我应该分享他们构建的令人敬畏的东西。

于 2014-09-08T16:20:41.663 回答