2012-05-07 58 views
6

我正在爲我的實體框架應用程序編寫一個簡單的搜索查詢。我需要檢查一堆字段是否爲空,如果不是,請對它們調用ToLower()並與搜索查詢進行比較。 LINQ查詢看起來是這樣的:有沒有一種簡單的方法在LINQ to Entities中編寫自定義函數?

public IQueryable<Store> SearchStores(string q, IQueryable<Store> source) 
{ 
    q = q.ToLower(); 

    return (
     from s in source 
     where (
      (s.Name != null && s.Name.ToLower().Contains(q)) || 
      (s.Description != null && s.Description.ToLower().Contains(q)) || 
      ... 
} 

有很多這樣的線,所以我很想寫一個輔助方法來清理了一下:

public static bool SafeSearch(this string s, string q) 
{ 
    return s == null ? false : s.ToLower().Contains(q); 
} 

這當然但是,由於LINQ to Entities並不理解安全搜索功能是什麼:

LINQ to Entities無法識別方法'Boolean SafeSearch(System.String,System.String)'方法,而且這種方法不能transla成爲商店的表現。

有沒有簡單的方法來寫這樣一個簡單的自定義函數?

謝謝!

+0

數據庫上的排序規則類型是什麼? – Brannon

回答

2

由於linq使用的表達式在你實際調用數據庫之前不會執行,所以你需要將你的函數包裝在一個謂詞中。

private static Func<Country, bool> Predicate(string q) 
{ 
    return x => (
     q.SafeSearch(x.Name) || 
     q.SafeSearch(x.Description) 
     ); 
} 

通過在查詢中調用SafeSearch擴展方法,還將處理x.Name爲null的情況。

public static class SearchExt 
{ 
    public static bool SafeSearch(this string q, string param) 
    { 
     return param == null ? false : param.ToLower().Contains(q); 
    } 
} 

,然後你可以用extesion方法

return source.Where(Predicate(q)); 

或使用LINQ表達

return from p in source 
     where Predicate(q).Invoke(p) 
     select p; 
+0

這是使用擴展方法,但是當我在linq中使用Invoke方法嘗試它時,我得到:LINQ to Entities不能識別該方法的布爾Invoke(Localsip.Models.Wine)方法,並且此方法不能翻譯成商店表達。有關於此的任何想法? – ManicBlowfish

+0

噢,好夠了。仍然想知道如何在linq查詢中包含「Predicate」Func。儘管感謝您的幫助。 – ManicBlowfish

+0

@ManicBlowfish你可以從源代碼中返回p。Where(Predicate(q))選擇p –

1

有一種方法來準備動態查詢和條件,並且還要用用它功能來構建它們的一部分。語法也是可讀的,這可以解決問題的「簡單」部分。有可能通過結合Linq表達式。有幾篇關於如何完成的文章,但我想我想出了一個新的方法。至少我沒有在網上找到它。

要繼續,您需要一個包含3個簡單功能的庫。他們使用System.Linq.Expressions.ExpressionVisitor來動態修改表達式。關鍵特性是將表達式中的參數統一起來,以便使具有相同名稱的兩個參數相同(UnifyParametersByName)。其餘部分將用給定表達式(ReplacePar)和輔助方法(NewExpr)替換命名參數。該圖書館提供github上的MIT許可證:LinqExprHelper,但您可以自行快速編寫一些內容。

首先定義一些方法,稍後可用於創建動態查詢。

public class Store 
{ 
    ... 

    public static Expression<Func<Store, bool>> 
     SafeSearchName(string sWhat) 
    { 
     return LinqExprHelper.NewExpr(
      (Store s) => s.Name != null && s.Name.ToLower().Contains(sWhat) 
     ); 
    } 

    public static Expression<Func<Store, bool>> 
     SafeSearchDesc(string sWhat) 
    { 
     return LinqExprHelper.NewExpr(
      (Store s) => s.Description != null && s.Description.ToLower().Contains(sWhat) 
     ); 
    } 
} 

然後你以這種方式查詢:

// Define a master condition, using named parameters. 
    var masterExpr = LinqExprHelper.NewExpr(
     (Store s, bool bSearchName, bool bSearchDesc) 
     => (bSearchName && bSearchDesc)); 

    // Replace stub parameters with some real conditions. 
    var combExpr = masterExpr 
     .ReplacePar("bSearchName", Store.SafeSearchName("b").Body) 
     .ReplacePar("bSearchDesc", Store.SafeSearchDesc("p").Body); 
     // Sometimes you may skip a condition using this syntax: 
     //.ReplacePar("bSearchDesc", Expression.Constant(true)); 

    // It's interesting to see how the final expression looks like. 
    Console.WriteLine("expr: " + combExpr); 

    // Execute the query using combined expression. 
    db.Stores 
     .Where((Expression<Func<Store, bool>>)combExpr) 
     .ToList().ForEach(i => { Console.WriteLine(i.Name + ", " + i.Description); }); 

我沒有在生產中使用這個還沒有,但一些簡單的測試都通過了。我沒有看到用這種方式組合查詢有任何限制。如果我們需要更多的參數,我們可以附加額外的組合級別。這種方法的優點是可以使用內聯的lambda表達式,這些表達式非常適合閱讀,還可以使用動態表達式創建和組合,這非常有用。

畢竟它「簡單」嗎?如果您認爲Linq的方法語法很簡單,那麼這幾乎就是這麼簡單。它不允許你創建自定義的Linq函數,但是可以提供類似的功能。

+0

表達式爲我做了訣竅 –

相關問題