2010-06-23 53 views
5

嗨我有一個名稱類型對象的集合,我想對其執行通配符搜索。例如,如果我提供搜索標準*ABC,則返回的名稱應以ABC開頭。如果我提供搜索標準ABC*,則返回的名稱應以ABC結尾。如果我提供搜索標準*ABC*,則返回的名稱應包含ABC。如果我提供搜索標準?ABC,則返回名稱的第二個,第三個和第四個字符應分別爲ABC,第一個字符可以是任何字符。使用WildCards字符(如*,%,?)進行LINQ搜索

+2

我們談論LINQ到對象這裏,或LINQ到SQL? – 2010-06-23 13:57:17

+0

@Samuel:看起來像LINQ-to-Objects,因爲他提到他有'收集對象'。 – ErikHeemskerk 2010-06-23 14:16:56

回答

10

這裏是您可以使用

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> MatchesWildcard<T>(this IEnumerable<T> sequence, Func<T,string> expression, string pattern) 
    { 
     var regEx = WildcardToRegex(pattern); 

     return sequence.Where(item => Regex.IsMatch(expression(item), regEx)); 
    } 

    public static string WildcardToRegex(string pattern) 
    { 
     return "^" + Regex.Escape(pattern). 
     Replace("\\*", ".*"). 
     Replace("\\?", ".") + "$"; 
    } 
} 

如下使用它的擴展方法:

void Main() 
{ 
    var items = new[] { new MyObj { MyProperty = "ABC123" }, 
         new MyObj { MyProperty = "123ABC" }, 
         new MyObj { MyProperty = "123ABC456" }, 
    }; 

    var matches = items.MatchesWildcard(item => item.MyProperty, "???ABC"); 
} 

public class MyObj 
{ 
    public string MyProperty {get;set;} 
} 

(從CodeProject採取WildcardToRegex)

+0

您的正則表達式方法(您從我的頭頂複製並鍵入)比我的好。 – ErikHeemskerk 2010-06-23 14:22:19

+0

@ErikHeemskerk - 爲什麼要學習RegEx語法,當你有谷歌? :-) – 2010-06-23 14:30:28

0

我認爲你需要使用。載,.StartWith,.EndsWith

3

我想你需要Regex.Escape和Regex.IsMatch()。

private IEnumerable<Item> FilterList(IEnumerable<Item> list, string query) 
{ 
    string pattern = QueryToRegex(query); 

    return list.Where(i => Regex.IsMatch(i.Name, pattern, RegexOptions.Singleline)); 
} 

private static string QueryToRegex(string query) 
{ 
    return "^" + Regex.Escape(query).Replace("\\*", ".*").Replace("\\?", ".") + "$"; 
} 

注:Samuel Jack's answer在他的正則表達式是更好的更好,所以在這裏可恥固定它。

+0

擊敗我38秒,+1! – 2010-06-23 14:18:50

+0

啊,但無論如何這一切都是徒勞的,因爲塞繆爾傑克發佈了一個更好的方法來將通配符表達式轉換爲正則表達式;我的工作不正常。 – ErikHeemskerk 2010-06-23 14:23:23

0

article列表擴展方法也兼容與實體框架和LINQ到實體。

用法示例:

var searchTerm = "*Inc"; 
var q = db.Customers 
     .WhereLike(c => c.CompanyName, searchTerm, '*') 
     .ToList(); 

的源代碼:

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Data.Objects; 
using System.Data.Objects.DataClasses; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

    public static class LinqExtensions 
    { 
     public static IQueryable<TSource> WhereLike<TSource>(
      this IQueryable<TSource> source, 
      Expression<Func<TSource, string>> valueSelector, 
      string value, 
      char wildcard) 
     { 
      return source.Where(BuildLikeExpression(valueSelector, value, wildcard)); 
     } 

     public static Expression<Func<TElement, bool>> BuildLikeExpression<TElement>(
      Expression<Func<TElement, string>> valueSelector, 
      string value, 
      char wildcard) 
     { 
      if (valueSelector == null) 
       throw new ArgumentNullException("valueSelector"); 

      var method = GetLikeMethod(value, wildcard); 

      value = value.Trim(wildcard); 
      var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value)); 

      var parameter = valueSelector.Parameters.Single(); 
      return Expression.Lambda<Func<TElement, bool>>(body, parameter); 
     } 

     private static MethodInfo GetLikeMethod(string value, char wildcard) 
     { 
      var methodName = "Contains"; 

      var textLength = value.Length; 
      value = value.TrimEnd(wildcard); 
      if (textLength > value.Length) 
      { 
       methodName = "StartsWith"; 
       textLength = value.Length; 
      } 

      value = value.TrimStart(wildcard); 
      if (textLength > value.Length) 
      { 
       methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith"; 
       textLength = value.Length; 
      } 

      var stringType = typeof(string); 
      return stringType.GetMethod(methodName, new Type[] { stringType }); 
     } 
    }