3

我看到很多类似于以下的代码

var customrs = MyDataContext.Customers.Where(...);
if (!String.IsNullOrEmpty(input)) {
  customers = customers.Where(c => c.Email.Contains(input));
}

我想把它放在一个扩展方法中,在调用 IQueryable 上的 Where 之前检查输入是否有效,以便可以像这样调用它

customers = MyDataContext.Customers.Where(...)
  .ContainsText(c => c.Email, input);

我的扩展方法看起来像这样

public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
  if (String.IsNullOrEmpty(text) {
    return source;
  }
  else {
    //do something here
  }
}

如何在解析的表达式上调用 Contains()?或者是否有另一种方法可以返回 IQueryable 的结果,其中解析的表达式包含解析的文本?

更新:它适用于 Linq to Sql

4

4 回答 4

5

tvanfosson有正确的想法来构建导致这个问题的答案的表达式。因此,为了完整起见,这是一个完整的工作解决方案

表达式生成器

public static class ExpressionBuilder {

  public static Expression<Func<T, bool>> ContainsText<T>(string propertyName, string text) {
    var paramExp = Expression.Parameter(typeof(T), "type");
    var propExp = Expression.Property(paramExp, propertyName);
    var methodInfo = typeof(string).GetMethod("Contains", new[] { typeof(string) });
    var valueExp = Expression.Constant(text, typeof(string));
    var methCall = Expression.Call(propExp, methodInfo, valueExp);
    return Expression.Lambda<Func<T, bool>>(methCall, paramExp);
  }

}

扩展方法

public static class IQueryableExtensions {

  public static IQueryable<T> ContainsText<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string text) {
    if (source == null) {
      throw new ArgumentNullException();
    }
    if (text.IsNullOrEmpty()) {
      return source;
    }
    string propName = ((MemberExpression)selector.Body).Member.Name;
    return source.Where(ExpressionBuilder.ContainsText<T>(propName, text));
  }

}

像这样调用

var customers = MyDataContext.Customers.Where(/* some code */)
  .ContainsText(c => c.Email, input);
于 2009-11-07T11:06:13.260 回答
3

我想我会在 String 上进行扩展,而不是在 IQueryable 上。

public static bool ContainsIfNotEmpty( this string source, string text )
{
    if (string.IsNullOrEmpty(text))
    {
       return true; // select all elements
    }
    if (string.IsNullOrEmpty(source))
    {
       return false; // select no elements
    }
    return source.Contains(text); // select only matching
}

然后将其用作:

customers = MyDataContext.Customers
                         .Where( c => c.Email.ContainsIfNotEmpty( input ) );

请注意,这需要 LINQ to 对象。如果您需要将它与 LINQ to SQL 一起使用,那么我建议使用构建器方法构建表达式。请注意,以下内容未经测试,因为我现在无法访问 VS。您可能想查看 Andrew Peters 的博客条目以获取类似示例和/或Expression类的文档。

public static class ExpressionBuilders
{
    public static Expression<Func<T,bool>> ContainsBuilder<T>( string column, string text )
    {
          ParameterExpression parameter = new Expression.Parameter( typeof(T), "t" );

          if (string.IsNullOrEmpty(text))
          {
              return (Expression<Func<T,bool>>)QueryExpression.Lambda( Expression.Constant( true ), parameter );
          }

          MethodInfo contains = typeof(T).GetMethod("Contains");
          Expression textExpression = Expression.Constant(text);
          Expression containsExpression = Expression.Call(parameter,contains,textExpression);

          return (Expression(Func<T,bool>))QueryExpression.Lambda( containsExpression, parameter );
    }
}

用作:

var predicate = ExpressionBuilders.ContainsBuilder<Customer>( "Email", input );

customers = MyDataContext.Customers.Where( predicate );
于 2009-11-06T15:06:15.550 回答
1

以下将是可能的使用IEnumerable<T>

public static IEnumerable<T> ContainsText<T>(
    this IEnumerable<T> source, Func<T, string> selector, string text)
{
  return source.Where(
      x => (!string.IsNullOrEmpty(selector(x)) &&
           (selector(x).Contains(text))));
}

为了留在IQueryable<T>您的唯一选择中,要么是 tvanfosson 建议的 Dynamic Linq,要么是User Defined Functions

但老实说,你还不如留在Where.

于 2009-11-06T15:07:24.023 回答
0

我认为你可以这样做:

customers = MyDataContext.Customers.Where(c =>  (string.IsNullOrEmpty(input) || c.Email.Contains(input)));
于 2009-11-06T14:51:12.373 回答