2010-04-11 47 views
3

我想使用Linq2Sql返回包含字符串列表中的值的所有行。 linq2sql類對象具有包含由空格分隔的單詞的字符串屬性。c#使用Linq2Sql查找表列中的匹配詞

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

例myProperty的值是:

MyObject1.MyProperty = "text1 text2 text3 text4" 
MyObject2.MyProperty = "text2" 

例如,使用一個字符串集合,我通過以下列表

var list = new List<>() { "text2", "text4" } 

這將在我的例子返回上面兩個項目,他們都包含「text2」值。

但是,我嘗試了下面的代碼,因爲我的擴展方法無法評估Linq2Sql。

public static IQueryable<MyObject> WithProperty(this IQueryable<MyProperty> qry, 
    IList<string> p) 
{ 
    return from t in qry 
     where t.MyProperty.Contains(p, ' ') 
     select t; 
} 

我也寫了一個擴展方法

public static bool Contains(this string str, IList<string> list, char seperator) 
{ 
    if (str == null) return false; 
    if (list == null) return true; 

    var splitStr = str.Split(new char[] { seperator }, 
     StringSplitOptions.RemoveEmptyEntries); 

    bool retval = false; 
    int matches = 0; 

    foreach (string s in splitStr) 
    { 
     foreach (string l in list) 
     { 
      if (String.Compare(s, l, true) == 0) 
      { 
       retval = true; 
       matches++; 
      } 
     } 
    } 

    return retval && (splitStr.Length > 0) && (list.Count == matches); 
} 

我如何能做到這一點任何幫助或想法?

回答

1

特殊照顧。擴展方法WithProperty的第一個參數必須爲IQueryable<MyObject>類型,而不是IQueryable<MyProperty>

無論如何,你不需要擴展方法的IQueryable。只需在lambda中使用Contains方法進行過濾。這應該工作:

List<string> searchStrs = new List<string>() { "text2", "text4" } 

IEnumerable<MyObject> myFilteredObjects = dataContext.MyObjects 
        .Where(myObj => myObj.MyProperty.Contains(searchStrs, ' ')); 

更新:

上面的代碼片段不會工作。這是因爲Contains方法不能轉換爲SQL語句。我想了一會兒問題,並通過考慮'我將如何在SQL中做到這一點'來解決問題:您可以通過查詢每個關鍵字並將所有結果聯合在一起來實現。令人遺憾的是,延遲執行Linq-to-SQL可以防止在一個查詢中執行這些操作。所以我想出了妥協的妥協方案。它查詢每一個關鍵字。這可以是以下之一:

  • 等於字符串
  • 兩個分隔符
  • 在字符串的開始和隨後之間由分隔符
  • 或在字符串的末尾和由分隔符組成

這跨越了一個有效的表達式樹,並且可以通過Linq-to-SQL轉換成SQL。查詢後,我不推遲執行,立即獲取數據並將其存儲在列表中。所有列表之後都會合並。

public static IEnumerable<MyObject> ContainsOneOfTheseKeywords(
     this IQueryable<MyObject> qry, List<string> keywords, char sep) 
{ 
    List<List<MyObject>> parts = new List<List<MyObject>>(); 

    foreach (string keyw in keywords) 
     parts.Add((
      from obj in qry 
      where obj.MyProperty == keyw || 
        obj.MyProperty.IndexOf(sep + keyw + sep) != -1 || 
        obj.MyProperty.IndexOf(keyw + sep) >= 0 || 
        obj.MyProperty.IndexOf(sep + keyw) == 
         obj.MyProperty.Length - keyw.Length - 1 
      select obj).ToList()); 

    IEnumerable<MyObject> union = null; 
    bool first = true; 
    foreach (List<MyObject> part in parts) 
    { 
     if (first) 
     { 
      union = part; 
      first = false; 
     } 
     else 
      union = union.Union(part); 
    } 

    return union.ToList(); 
} 

並使用它:

List<string> searchStrs = new List<string>() { "text2", "text4" }; 

IEnumerable<MyObject> myFilteredObjects = dataContext.MyObjects 
        .ContainsOneOfTheseKeywords(searchStrs, ' '); 

這種解決方案確實比一切其他優雅。對於10個關鍵字,我必須查詢數據庫10次,每次捕獲數據並將其存儲在內存中。這是在浪費記憶,並且表現不佳。我只是想證明它可能在Linq中(也許它可以在這裏或那裏進行優化,但我認爲它不會完美)。

我強烈建議將該函數的邏輯交換爲數據庫服務器的存儲過程。一個查詢,由數據庫服務器優化,並且不浪費內存。

另一種選擇是重新考慮你的數據庫設計。如果您想要查詢一個字段的內容(您將這個字段當作關鍵字數組,用空格分隔),您可能只是選擇了不適當的數據庫設計。您更希望使用您的表的外鍵創建一個新表。新表格只有一個關鍵字。查詢將是太多更簡單,更快,更容易理解。

+0

沒有抱歉,這不起作用。我的自定義Contains方法無效(這只是解釋我在做什麼的一個例子),因爲它不能被賦值,因爲它不能被轉換爲SQL。 – David 2010-04-11 11:54:14

+0

你說得對,我更新了答案。 – 2010-04-11 14:18:26

+0

第二個被剪切的代碼現在可以正常工作,只需使用示例數據庫對其進行測試即可。 – 2010-04-11 14:20:34

0

我沒有試過,但如果我沒有記錯,這應該工作:

from t in ctx.Table 
where list.Any(x => t.MyProperty.Contains(x)) 
select t 

可以用All()取代Any()如果你想在list所有字符串匹配

編輯:

爲了澄清我正在試圖用這個做什麼,這裏是一個類似的查詢沒有linq編寫,以解釋使用All a第二Any

where list.Any(x => t.MyProperty.Contains(x)) 

翻譯爲:

where t.MyProperty.Contains(list[0]) || t.MyProperty.Contains(list[1]) || 
     t.MyProperty.Contains(list[n]) 

而且

where list.Any(x => t.MyProperty.Contains(x)) 

翻譯爲:在正確的軌道上

where t.MyProperty.Contains(list[0]) && t.MyProperty.Contains(list[1]) && 
     t.MyProperty.Contains(list[n]) 
+1

我不確定這是否合理,因爲MyProperty包含一個完整的字符串「text1 text2 text3」,需要將其分解爲單獨的值,然後與列表進行比較。我試過你的建議,並得到以下錯誤:除了包含()運算符 – David 2010-04-11 11:44:24

+0

您的答案與問題無關!本地序列不能用於查詢運算符的LINQ to SQL實現!它不起作用,Any和All操作符完成與OP想要的完全不同的操作。 – 2010-04-11 14:26:31

+0

@phild:OP已經說過它不起作用,但Any和All操作符在使用這種方式時是正確的。在我的回答中看到我的解釋。如果不是他提到的錯誤,這種方式只適用於 – 2010-04-12 07:26:00