为搜索构建动态表达式



我遇到了麻烦,无法弄清楚似乎是一件非常简单的事情,在普通SQL中可以在1分钟内完成,到目前为止已经几个小时了。情况如下:

  • 我有一个字段,用户可以输入尽可能多的单词,因为他/她喜欢
  • 我需要构建表达式来查找匹配
  • 假设数据库中有3个字段:名,名,姓
  • 我需要拆分搜索条目并与这3个字段进行比较
  • 我正在处理Silverlight RIA, EF
  • 再次搜索条目包含未知数量的单词

在我想要完成的下面,返回类型是强制性的:

public Expression<Func<MyEntity, bool>> GetSearchExpression(string text)
{
    Expression<Func<MyEntity, bool>> result;
    var keywords = text.Trim().Split(" ");
    foreach(var keyword in keywords)
    {
         // TODO: 
         // check whether 'OR' is required (i.e. after second loop)
         // (firstname = 'keyword'
         // AND
         // middlename = 'keyword'
         // AND
         // lastname = 'keyword')
         // OR
         // (firstname like '%keyword%'
         // AND
         // middlename like '%keyword%'
         // AND
         // lastname like '%keyword%')
    }
    return result;
}

提前感谢!

最简单的方法是使用Joe Albahari的PredicateBuilder来做这样的事情:

var predicate = PredicateBuilder.False<MyEntity>();
foreach (string keyword in keywords)
{
    string temp = keyword;
    predicate = predicate.Or (
        p => p.FirstName.Contains (temp) && 
             p.LastName.Contains (temp) &&
             p.MiddleName.Contains (temp));
}
return predicate;

我省略了相等性检查,因为"Contains"(即like '%...%')无论如何都会涵盖这种可能性。

但是,我必须指出,从业务逻辑的角度来看,您的条件没有任何意义。在什么情况下,你想找一个名字、姓氏和中间名都包含"约翰"的人?我怀疑你真正想要的是这样的东西:

var predicate = PredicateBuilder.True<MyEntity>();
foreach (string keyword in keywords)
{
    string temp = keyword;
    predicate = predicate.And (
        p => p.FirstName.Contains (temp) || 
             p.LastName.Contains (temp) ||
             p.MiddleName.Contains (temp));
}
return predicate;

最后一个注意事项:因为PredicateBuilder要求您在创建查询时调用.AsExpandable(),我不知道这是否适合您。您可能不得不求助于构建自己的表达式,这可能有点乏味。这可以让你开始:

var pParam = Expression.Parameter(typeof(MyEntity), "p");
var predicate = Expression.Constant(true);
foreach (string keyword in keywords)
{
    var keywordExpr = Expression.Constant(keyword);
    // TODO: create an expression to invoke .FirstName getter
    // TODO: create an expression to invoke string.Contains() method
    //TODO: do the same for lastname and middlename
    predicate = Expression.And(predicate, 
        Expression.Or(
            Expression.Or(firstNameContainsKeyword,
                middleNameContainsKeyword),
            lastNameContainsKeyword));
}
return Expression.Lambda<Func<MyEntity, bool>>(predicate, pParam);

最新更新