2010-09-21 253 views
15

我試圖找出一種方法來查詢我的數據模型中的一個對象,並且只包含那些非空的參數。如下圖所示:在LINQ查詢中執行條件'if'語句'where'語句

public List<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    List<Widget> widgets = (from w in db.Widgets 
          where 
           ... if cond1 != null w.condition1 == cond1 ... 
           ... if cond2 != null w.condition2 == cond2 ... 
           ... if cond3 != null w.condition3 == cond3 ... 
          select w).ToList(); 
    return widgets; 
} 

由於部件表可以得到非常大的,我想避免這樣做:

public List<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    List<Widget> widgets = db.Widgets.ToList(); 

    if(cond1 != null) 
     widgets = widgets.Where(w => w.condition1 == cond1).ToList(); 

    if(cond2 != null) 
     widgets = widgets.Where(w => w.condition2 == cond2).ToList(); 

    if(cond3 != null) 
     widgets = widgets.Where(w => w.condition3 == cond3).ToList(); 

    return widgets; 
} 

我看了幾個例子,但沒有真正看到任何匹配我需要做的事情。

回答

29

要避免實際執行查詢,直到你準備什麼:

public List<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    var widgets = db.Widgets; 

    if(cond1 != null) 
     widgets = widgets.Where(w => w.condition1 == cond1); 

    if(cond2 != null) 
     widgets = widgets.Where(w => w.condition2 == cond2); 

    if(cond3 != null) 
     widgets = widgets.Where(w => w.condition3 == cond3); 

    return widgets.ToList(); 
} 

注意ToList呼叫的方式去除。直到您開始迭代查詢纔會執行查詢。調用ToList將強制執行該操作,以便將結果放入List<>並返回。我甚至會建議該方法的返回值更改爲IEnumerable<Widget>以及跳過到底ToList電話:

public IEnumerable<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    var widgets = db.Widgets; 

    if(cond1 != null) 
     widgets = widgets.Where(w => w.condition1 == cond1); 

    // [...] 

    return widgets; 
} 

這樣調用代碼獲取來決定何時執行查詢(它甚至可以添加更多的條件在此之前)。

+0

是否由JIT編譯器在結果查詢中刪除條件?或者linq'保證'一些優化? – xtofl 2010-09-21 16:51:43

+0

@xtofl:不確定你的意思?你想擺脫哪些條件? – 2010-09-21 17:49:02

+0

如果條件不滿足,它們不會添加到表達式樹中。 – Michael 2010-09-22 02:02:43

2

這樣的事情呢?

 IEnumerable<Widget> condQuery = (from w in db.Widgets); 
     if(cond1 != null) condQuery = condQuery.Where(w=> w.condition1 == cond1); 
     if(cond2 != null) condQuery = condQuery.Where(w=> w.condition2 == cond2); 

etc ...?

+0

不編譯。 – Timwi 2010-09-21 13:59:52

1

你實際上要求調度員 linq查詢。 Where方法接受一個謂詞,因此您可以在創建查詢之前構建謂詞。

- 編輯 - 起初,我認爲它更容易,寫了一些甚至沒有編譯的僞代碼。現在,無論如何,我想我明白了。此代碼將起作用;它將構建where子句與應用它分開。

static Predicate<Widget> combine( 
      Predicate<Widget> existing, 
      Predicate<Widget> condition) 
    { 
     var newpred = new Predicate<Widget>(w=> existing(w) && condition(w)); 
     return newpred; 

    } 

,並使用類似的這種 '建築' 功能:

static void Main(string[] args) 
    { 
     string cond1 = "hi"; 
     string cond2 = "lo"; 
     string cond3 = null; 
     var pr = new Predicate<Widget>((Widget w) => true); 
     if (cond1 != null) pr = combine(pr, w => w.condition1 == cond1); 
     if (cond2 != null) pr = combine(pr, w => w.condition2 == cond2); 
     if (cond3 != null) pr = combine(pr, w => w.condition3 == cond3); 

我有一個小幫手陣列進行了測試:

 var widgets = new Widget[]{ 
      new Widget(){ condition1 = "" }, 
      new Widget(){ condition1 = "hi", condition2 = "lo" } 
     }; 

     var selected = widgets.Where((w) => pr(w)); 

     foreach (var w in selected) { 
      Console.WriteLine(w); 
     } 
+0

不編譯。 – Timwi 2010-09-21 13:59:24

+0

另外,更改邏輯。 OP檢查所有非空條件,這隻檢查第一個。 OP還表示,條件是字符串,而這將它們視爲布爾值。 – curveship 2010-09-21 14:19:46

+0

即使您確實修復了編譯錯誤(字符串不能隱式轉換爲bool),該版本的程序與原始版本的語義也有很大不同。這個版本找到了可以測試並且僅使用它的第一個條件;原始版本應用了所有可能的條件。 – 2010-09-21 14:21:07

20

請使用 「或門」:前言我們小部件條件測試用「||」並檢查我們是否使用該條件。如果我們不是,那麼「或」的後半部分就不會被評估。這就是爲什麼它是一個大門 - 如果第一部分評估爲真,我們就不會再進一步​​了。

如果我正在寫它,我會像下面這樣做。我用var syntatic糖來保存LINQ查詢並將ToList()移到最後。

public List<Widget> GetWidgets(string cond1, string cond2, string cond3) 
{ 
    MyDataContext db = new MyDataContext(); 
    var widgets = from w in db.Widgets 
        where (cond1 == null || w.condition1 == cond1) 
        && (cond2 == null || w.condition2 == cond2) 
        && (cond3 == null || w.condition3 == cond3) 
        select w; 
    return widgets.ToList(); 
} 

編輯:語法

+0

難以理解。但美麗的代碼。 – Sagi 2015-12-06 17:18:53

+1

這是最好的解決方案,並將在一個SQL語句中執行。 – cmartin 2016-01-22 12:59:05

+1

這是更好的解決方案,用於指出ToList部分的+1。 – 2016-08-04 20:09:05

0

我們可以用很簡單的方法如下圖所示。

(from e in employee 
join d in departments on e.departmentId equals d.departmentId 
Select new { 
e.name, 
d.name, 
getEmployeeContacts(e) 
} 
//return active contact if not return first . This is same like if else along with null check 
private contact getEmployeeContacts(Employee e) 
{ 
return e.Contacts.FirstOrDefault(x => x.Active == 1) ?? e.Contacts.FirstOrDefault(); 
}