2011-04-26 31 views
17

我知道一個典型的方法是這樣的:LINQ:添加where子句僅當值不爲空

IQueryable query = from staff in dataContext.Staffs; 
if(name1 != null) 
{ 
    query = from staff in query where (staff.name == name1); 
} 

然而,我們從其他開發商接手一個項目,我們看到了這樣的代碼:

IQueryable query = from staff in dataContext.Staffs; 
query = from staff in query where (name1 == null || staff.name == name1); 

如果這是一個正常的SQL語句,我肯定會說第二個是不好的做法。因爲它在name1爲null時向查詢添加無意義的where子句。

但我是新來的LINQ,所以我不確定LINQ是不同的?

+0

您可以使用SQL Server Profiler查看查詢歸結到的內容。 – alex 2011-04-26 08:34:39

+1

在SQL中,它並不重要,因爲「name1」在運行時是已知值,查詢優化器將優化WHERE子句。事實上,您始終都會在SQL中看到這種風格。在LINQ中這樣做的好處是代碼更簡潔。它可能不會更改由LINQ-to-SQL生成的SQL語句(它可能已經消除了它),並且它絕對不會影響SQL語句的性能,即使它停留在那裏。 – 2011-04-26 11:17:08

+0

所以我一開始就錯了...... 瞭解了,謝謝~~! – Henry 2011-04-28 07:46:56

回答

10

通常,使用流暢的語法而不是查詢語法來寫這種事情會更順暢。

例如

IQueryable query = dataContext.Staffs; 
if(name1 != null) 
{ 
    query = query.Where(x => x.name == name1); 
} 

所以,如果name1爲空,你乾脆不要做任何Where()電話。如果您有多個不同的過濾器,所有這些過濾器都可能需要也可能不需要,也可能有各種不同的排序順序,我發現這變得更容易管理。

編輯爲alex:好吧,我只是回答有關添加一個where子句只有當一個值不爲null的問題。爲了回答這個問題的其他部分,我使用Entity Framework 4試驗了一下,看看LINQ產生了什麼SQL。您可以將query轉換爲ObjectQuery並致電.ToTraceString()。結果表明,該WHERE條款出來如下:

WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1 

所以,是的,這是典型的壞SQL,如果你對name列的索引,不希望它被使用。

編輯#2:再次嘗試使用LINQ to SQL而不是實體框架,結果相當不同。這一次,試圖將name1爲空的查詢根本不會導致WHERE條款,正如您所期望的那樣;試圖用name1爲「a」導致簡單的WHERE [t0].[name] = @p0@p0作爲「a」發送。因此實體框架不是似乎優化。這有點令人擔憂。

+3

這似乎並不能回答這個問題。 – alex 2011-04-26 08:33:42

+1

這不是很清楚問題是什麼 – Vedran 2011-04-26 08:42:18

+0

我沒有看到你的答案是如何相關的。 – 2011-04-26 08:48:35

2

LINQ是其他一些原因(在這導致), LINQ,就是以「更快的方式」數據與LITTEL代碼和明確的鱈魚作爲LINQ的可能,出現了許多好處的方式不同勢:

  1. 使數據轉換成對象更容易。我相信你已經聽說過「Impedence Mismatch」這個詞經常被使用,這意味着LINQ可以減少在面向對象代碼和數據範例(如分層,平面文件,消息等)之間進行翻譯所需的工作量。關係等等。它不會消除「Impedence Mismatch」,因爲您仍然必須以原生形式推理您的數據,但從這裏到那裏的橋樑(IMO)要短得多。

  2. 所有數據的通用語法。一旦你學習了查詢語法,你可以在任何LINQ提供者中使用它。我認爲這是一個比多年來隨着數據訪問技術而增長的巴別塔更好的開發模式。當然,每個LINQ提供程序都有必要的細微差別,但基本方法和查詢語法是相同的。

  3. 強類型代碼。 C#(或VB.NET)查詢語法是該語言的一部分,您可以使用C#類型進行編碼,這些類型被轉換爲提供程序可以理解的內容。這意味着您在開發生命週期的早期比其他地方獲得編譯器發現錯誤的工作效率。當然,存儲過程中的許多錯誤會在保存時生成錯誤,但是LINQ比SQL Server更通用。您必須考慮所有其他類型的生成運行時錯誤的數據源,因爲它們的查詢由字符串或其他鬆散類型的機制組成。

  4. 供應商整合。將數據源拉到一起非常容易。例如,對於一些非常複雜的場景,您可以使用LINQ to Objects,LINQ to SQL和LINQ to XML。我認爲它非常優雅。

  5. 工作減少。在LINQ之前,我花了很多時間構建DAL,但現在我的DataContext是DAL。我也使用過OPF,但是現在我有了LINQ,它可以與箱子中的多個提供商以及其他許多第三方提供商一起提供,這爲我提供了前幾點的好處。我可以在一分鐘內建立一個LINQ to SQL DataContext(與我的電腦和IDE一樣快)。

  6. 一般情況下的性能不成問題。 SQL Server最近優化了查詢,就像存儲過程一樣。當然,仍然有些情況下出於性能原因需要存儲過程。例如,我發現使用存儲過程時更智能,當我在表之間進行多次交互時使用事務內的附加邏輯。嘗試在代碼中執行相同任務的通信開銷,除了讓DTC參與分佈式事務之外,使存儲過程的選擇更加引人注目。但是,對於在單個語句中執行的查詢,LINQ是我的首選,因爲即使存儲過程的性能收益很小,以前各點(IMO)的好處也會帶來更多的權重。

  7. 內置安全性。在LINQ之前我首選存儲過程的一個原因是他們強制使用參數,有助於減少SQL注入攻擊。 LINQ to SQL已經對輸入進行了參數化,這同樣安全。

  8. LINQ是聲明式的。很多關注LINQ to XML或LINQ to SQL的工作,但LINQ to Objects非常強大。 LINQ to Objects的典型示例是從字符串[]中讀取項目。但是,這只是一個小例子。如果您考慮每天使用的所有IEnumerable集合(您也可以查詢IEnumerable),那麼機會就會非常豐富。即搜索所選項目的ASP.NET ListBox控件,對兩個集合執行集合操作(​​例如Union),或迭代List並在每個項目的ForEach中運行lambda。一旦你開始在本質上是聲明性的LINQ中思考,你可以發現許多任務比現在使用的命令式技術更簡單,更直觀。

我可能會繼續,但我最好停在那裏。希望這將提供一個更積極的觀點,您可以更加高效地使用LINQ,並可能從更廣泛的角度將其視爲有用的技術。

15

,如果你的第一個條件計算爲false

更新,你可以把它寫像

IQueryable query = from staff in dataContext.Staffs; 
query = from staff in query where (name1 != null && staff.name == name1); 

這樣你的病情的第二部分將不進行評估:
如果你寫

IQueryable query = from staff in dataContext.Staffs; 
    query = from staff in query where (name1 == null || staff.name == name1); 

和name1爲null您的條件的第二部分將不會被評估s因斯或條件只需要一個條件,返回true

PLZ看到這樣link進一步詳細

+1

如果'name1'爲空,他希望返回所有'Staffs'記錄:在這種情況下,您的重寫將不會返回任何內容。 – Carson63000 2011-04-26 10:20:50

+0

@ Carson63000謝謝提及 – 2011-04-26 10:30:38

+0

爲什麼這個答案不是被否定的否定?當時,你認爲你在這裏回答什麼問題?...問題是'name1',作爲應用程序範圍中的一個變量,是否正在被轉換爲sql並進行求值......它是哪個?'@ p__linq__0 IS NULL'檢查; '@ p_linq_0'這裏是作爲參數傳遞的變量。 – 2016-11-24 05:33:07

0

不,我不是強烈同意你的看法。 在這裏你只給一個簡單的邏輯

if(name1 != null) 
// do your stuff 

,但如果你做的東西與擁有空值的名稱1不同會發生什麼.. !!好吧,現在考慮這種情況。 在本例中,您將展示如何處理源集合中可能的空值。 對象集合(如IEnumerable<T>)可以包含值爲null的元素。 如果源集合爲空或包含值爲空的元素, 並且您的查詢不處理空值,則在執行查詢時將引發NullReferenceException

也許這可能是一個問題......

0

我已經看到了標準的SQL這種模式,如果你有幾個參數,這些參數可以爲NULL,似乎是有用的。例如:

SELECT * FROM People WHERE (@FirstName IS NULL OR FirstName = @FirstName) 
         AND (@LastName IS NULL OR LastName = @LastName) 

如果你在LINQ中看到這個,可能他們只是盲目地翻譯了他們以前的SQL查詢。

+0

該模式的問題在於它不適合查詢優化器。例如,如果「FirstName」或「LastName」上有索引,則不會使用它,因爲根據傳入的參數,「WHERE」子句可能涉及或不涉及表中的那些列。 – Carson63000 2011-04-26 10:22:26

1

我喜歡用表達式

Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true; 

    if (DateBirth.HasValue) 
       { 
        Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth); 
        expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate); 
       } 

IQueryable query = dataContext.Persons; 
query = query.Where(expresionFinal); 
+0

差不多一個答案....應該解釋你爲什麼不做'expresionDate = c =>(EntityFunctions.TruncateTime(c.DateBirth)== DateBirth)|| DateTime.HasValue'這裏.. – 2016-11-24 05:39:04

+0

我不會建議,它會影響查詢性能。如果用戶沒有通知日期不過濾 if(DateBirth.HasValue)< - 是一個搜索參數 – J4ime 2016-11-25 09:13:17

2

要做到這一點,最好的方法是創建自己的擴展方法,將採取在條件語句和地方表達。如果條件爲真,那麼它將使用where表達式,否則它將不使用它。這可以顯着清理你的代碼,消除if語句的需要。

public static class LinqExtensions 
{ 
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause) 
    { 
     if (condition) 
     { 
      return query.Where(whereClause); 
     } 
     return query; 
    } 
} 

現在你可以寫你這樣的代碼:

IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1); 
+0

這是一個非常好的主意,但我得到的錯誤:其他信息:LINQ to Entities無法識別方法Where。 ....並且此方法無法轉換爲商店表達式。 Sooo,EF無用我使用這個確切的方法沒有錯誤。如果你給了我更多的信息,我可能會幫助你。確保你首先把你的DbSet,IEnumerable,ICollection或List變成IQueryable,否則C#將無法找到WhereIf擴展方法。 (編輯**我意識到我忘記了在我的例子中調用AsQueryable(),我添加了它,希望這有幫助,並且我更改了Where WhereIf的名稱,並且更好地描述了該方法。) – 2016-11-23 09:51:04

+0

問題是使用Linq實體框架上的表達式使用後端Linq到實體映射器,並且每個使用的linq擴展方法只有在支持對TSQL(或使用任何一種DBMS的語言)進行翻譯時才能使用。你的擴展方法(我也稱爲WhereIf)在任何內部類型上都可以正常工作,但是對於EF ...沒有骰子。我把我的解決方案作爲答案 – 2016-11-23 21:55:41

+0

當然,我遇到過幾次不同的查詢都遇到過這個問題。不過,我使用EF6,它有一個edmx文件,其中包含所有的實體模型。我在所有的查詢中都使用了這個技巧,它可以將它轉換爲t-sql;我甚至可以打印出它生成的sql查詢。 (我也只是用Linq to SQL來測試它,它工作)。我懷疑它很重要,但我正在使用Azure SQL,而不是SQL Server。無論如何,我希望你弄明白或找到適合你的解決方案。如果我可以幫助讓我知道。 -Cheers – Soto 2016-11-23 22:48:54

0

所以,我想在這裏列爲答案.Where(..., x => ...)擴展方法,但它並沒有對實體框架的工作爲LINQ到實體沒有按」不知道如何將其轉換爲TSQL。

因此,這裏是我的解決方案讓我的Func鍵上:

Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes 
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue)) 
{ 
    columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue; 
} 

_context.SomeEfPocos.Where(x => ..... && 
      ..... && 
      ..... &&) 
.Where(columnBeingFilteredPredicate); 

someColumnBeingFilteredValue在我的情況是在爲NULL默認值的封裝方法的字符串參數。

+0

這是不適用於這個問題或該擴展的表示形式..問題和擴展的前提是使用'someColumnBeingFilteredValue'與一個單獨的謂詞。這裏的前提是** NOT **有一個本地變量條件檢查的sql翻譯;因此,在應用程序之外沒有考慮這樣的檢查或評估。 – 2016-11-24 13:10:54

0

對於EF核心我打破了它像這樣:

IQueryable<Partners> recs = contextApi.Partners; 
if (status != -1) 
{ 
    recs = recs.Where(i => i.Status == status); 
} 
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName); 
foreach (var rec in recs) 
{ 
} 

我必須明確的用我的打字而不是依靠var