将Linq to EF查询构建为可变数量的列中的变量关键字列表?

时间:2017-11-20 19:55:32

标签: c# entity-framework linq predicatebuilder

我正在尝试构建一个实用程序方法来构建Linq查询或Linq谓词,以添加到Linq到EF查询,以搜索可变数量的列中的术语列表中的所有术语。

我正在尝试使用PredicateBuilder来构建where子句。使用一个搜索项和一个固定的列列表,相对容易。 我正在尝试处理的伪代码到目前为止看起来像这样:

private static Predicate<Project> CreateDynamicSearch(IEnumerable<strings> searchableColumns, string[] searchTerms)
{
      var predicate = PredicateBuilder.True<Project>();
      foreach (var columnName in searchableColumns) 
      { 
        foreach (var term in searchTerms)
        {
          predicate = predicate.And(a => a.**columnName**.Contains(term));
        }
       predicate = predicate.Or(predicate);
      }
      return predicate;
}

我遇到的最大问题是处理 columnName 的表达式。以前的建议是使用表达式树,但我不明白它在这种情况下是如何工作的。

**更新** 我已经在更新后获取了代码。它构建但是当我实际调用 Extension.Property(param,columnName); 行时出现错误,错误 Instance属性'Name'没有为类型'System定义。 Func`2 [Myclass,System.Boolean]'消息。 columnName =“Name”

**更新2 ** 它被称为的方式:

var test = CreateDynamicSearch<Func<Project, bool>>(searchCols, searchTerms);

1 个答案:

答案 0 :(得分:2)

您可以自己为谓词构建表达式,在这种情况下它相对容易:

private static Expression<Func<T, bool>> CreateDynamicSearch<T>(IEnumerable<string> searchableColumns, string[] searchTerms) {
    // start with true, since we combine with AND
    // and true AND anything is the same as just anything
    var predicate = PredicateBuilder.True<T>();
    foreach (var columnName in searchableColumns) {                
        // start with false, because we combine with OR
        // and false OR anything is the same as just anything
        var columnFilter = PredicateBuilder.False<T>();
        foreach (var term in searchTerms) {
            // a =>
            var param = Expression.Parameter(typeof(T), "a");
            // a => a.ColumnName
            var prop = Expression.Property(param, columnName);
            // a => a.ColumnName.Contains(term)
            var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(term));
            columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
        }
        predicate = predicate.And(columnFilter);
    }
    return predicate;
}

回应评论

  

我很好奇是否有某种方法你可以把它结合起来   由Expression.Property(param,columnName)创建的表达式   编译器生成的一个(字符串s) - &gt; s.Contains(术语)

你可以这样做(例如):

// a =>
var param = Expression.Parameter(typeof(T), "a");                    
// a => a.ColumnName
var prop = Expression.Property(param, columnName);                    
// s => s.Contains(term)
Expression<Func<string, bool>> contains = (string s) => s.Contains(term);
// extract body - s.Contains(term)
var containsBody = (MethodCallExpression)contains.Body;                    
// replace "s" parameter with our property - a.ColumnName.Contains(term)
// Update accepts new target as first parameter (old target in this case is 
// "s" parameter and new target is "a.ColumnName")
// and list of arguments (in this case it's "term" - we don't need to update that).
// 
var call = containsBody.Update(prop, containsBody.Arguments);
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));