为搜索构建动态表达式

时间:2011-07-07 16:58:08

标签: silverlight entity-framework expression ria

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

  • 我有单个字段,用户可以输入他/她喜欢的字数
  • 我需要构建Expression才能找到匹配
  • 假设数据库中有3个字段:firstname,middlename,lastname
  • 我需要拆分搜索条目并与这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;
}

提前致谢!

1 个答案:

答案 0 :(得分:1)

最简单的方法是使用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;

我省略了平等检查,因为“包含”(即like '%...%')无论如何都会涵盖这种可能性。

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

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