[英]LINQ: adding where clause only when a value is not null
我知道一個典型的方式是這樣的:
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 為空時向查詢添加了一個無意義的 where 子句。
但我是 LINQ 的新手,所以我不確定 LINQ 是否不同?
你可以這樣寫
IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);
這樣,如果您的第一個條件評估為 false,則不會評估您的條件的第二部分
更新:
如果你寫
IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);
並且 name1 為 null 您的條件的第二部分將不會被評估,因為 or 條件只需要一個條件才能返回 true
請參閱此鏈接以獲取更多詳細信息
通常,使用流利的語法而不是查詢語法來編寫這種東西感覺更流暢。
例如
IQueryable query = dataContext.Staffs;
if(name1 != null)
{
query = query.Where(x => x.name == name1);
}
因此,如果name1
為空,您就無需執行任何Where()
調用。 如果您有多個不同的過濾器,所有這些可能需要也可能不需要,並且可能有各種不同的排序順序,我發現這變得更易於管理。
為 alex 編輯:好的,我正在回答有關僅在值不為空時添加 where 子句的問題。 為了回答問題的另一部分,我使用 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 而不是 Entity Framework 再次嘗試,結果完全不同。 這一次,嘗試name1
為 null 的查詢根本不會產生WHERE
子句,正如您所希望的那樣; 嘗試將name1
“a”會導致一個簡單的WHERE [t0].[name] = @p0
和@p0
作為“a”發送。 實體框架似乎沒有因此優化。 這有點令人擔憂。
最好的方法是為自己創建一個擴展方法,該方法將接受條件語句和 where 表達式。 如果條件為真,那么它將使用 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);
因此,我嘗試了此處列出的.Where(..., x => ...)
擴展方法作為答案,但它不適用於實體框架,因為 Linq To Entities 不知道如何將其轉換為 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。
我喜歡使用表達式,例如
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);
對於 EF Core,我將其分解為:
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
。
LINQ 在其他一些原因上有所不同(不是在這個原因中),LINQ 是一種以“更快的方式”獲取數據的方式,使用盡可能少的代碼和清晰的代碼,LINQ 有很多好處:
更容易將數據轉換為對象。 我相信您已經聽說過經常使用“阻抗不匹配”這個術語,這意味着 LINQ 減少了在面向對象代碼和數據范例(例如分層、平面文件、消息、關系,等等。 它並沒有消除“阻抗不匹配”,因為您仍然必須以原始形式對數據進行推理,但是從這里到那里的橋梁(IMO)要短得多。
所有數據的通用語法。 一旦學習了查詢語法,就可以將它與任何 LINQ 提供程序一起使用。 我認為這是一個比多年來隨着數據訪問技術而發展起來的巴別塔更好的開發范式。 當然,每個 LINQ 提供程序都有必要的獨特細微差別,但基本方法和查詢語法是相同的。
強類型代碼。 C#(或 VB.NET)查詢語法是語言的一部分,您使用 C# 類型進行編碼,這些類型被翻譯成提供者可以理解的內容。 這意味着您可以讓編譯器在開發生命周期中比其他地方更早地發現錯誤,從而提高工作效率。 當然,存儲過程語法中的許多錯誤會在您保存時產生錯誤,但 LINQ 比 SQL Server 更通用。 您必須考慮生成運行時錯誤的所有其他類型的數據源,因為它們的查詢是由字符串或其他一些松散類型的機制形成的。
提供商集成。 將數據源整合在一起非常容易。 例如,對於一些非常復雜的場景,您可以一起使用 LINQ to Objects、LINQ to SQL 和 LINQ to XML。 我認為它非常優雅。
減少工作。 在 LINQ 之前,我花了很多時間構建 DAL,但現在我的 DataContext 是 DAL。 我也使用過 OPF,但現在我有 LINQ,它附帶多個提供程序和許多其他 3rd 方提供程序,讓我從之前的觀點中受益。 我可以在一分鍾內設置一個 LINQ to SQL DataContext(我的計算機和 IDE 可以跟上的速度)。
一般情況下的性能不會成為問題。 SQL Server 現在可以很好地優化查詢,就像存儲過程一樣。 當然,仍然存在出於性能原因需要存儲過程的情況。 例如,當我在表之間進行多次交互時,我發現使用存儲過程會更聰明,並且事務內部有額外的邏輯。 除了讓 DTC 參與分布式事務之外,嘗試在代碼中執行相同任務的通信開銷使存儲過程的選擇更具吸引力。 但是,對於在單個語句中執行的查詢,LINQ 是我的首選,因為即使存儲過程的性能提升很小,前面幾點 (IMO) 的好處也更重要。
內置安全性。 在 LINQ 之前我更喜歡存儲過程的一個原因是它們強制使用參數,有助於減少 SQL 注入攻擊。 LINQ to SQL 已經參數化輸入,這同樣安全。
LINQ 是聲明性的。 使用 LINQ to XML 或 LINQ to SQL 非常關注,但 LINQ to Objects 非常強大。 LINQ to Objects 的一個典型示例是從 string[] 中讀取項目。 然而,這只是一個小例子。 如果您考慮您每天使用的所有 IEnumerable 集合(您也可以查詢 IEnumerable),那么機會很多。 即在 ASP.NET ListBox 控件中搜索選定項,對兩個集合執行集合操作(例如 Union),或者遍歷 List 並在每個項的 ForEach 中運行 lambda。 一旦您開始使用本質上是聲明性的 LINQ 進行思考,您會發現您的許多任務比您今天使用的命令式技術更簡單、更直觀。
我可能可以繼續,但我最好停在那里。 希望這將為您如何提高使用 LINQ 的工作效率提供更積極的觀點,並可能從更廣泛的角度將其視為一種有用的技術。
我在標准 SQL 中看到過這種模式,如果您有幾個可能為 NULL 的參數,它似乎很有用。 例如:
SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
AND ( @LastName IS NULL OR LastName = @LastName )
如果您在 LINQ 中看到這一點,那么他們可能只是盲目地翻譯了他們的舊 SQL 查詢。
我喜歡擴展的想法
public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
=> condition ? query.Where(whereClause) : query;
不,我不太同意你的觀點。 在這里你只是給出了一個簡單的邏輯
if(name1 != null)
// do your stuff
但是,如果您對具有空值的 name1 執行不同的操作會發生什么......!! 好的,現在考慮這種情況。 在此示例中,您將展示如何處理源集合中可能的空值。 像IEnumerable<T>
這樣的對象集合可以包含值為 null 的元素。 如果源集合為 null 或包含值為 null 的元素,並且您的查詢不處理 null 值,則執行查詢時將引發NullReferenceException
。
可能這可能是一個問題......
我使用下面的擴展方法。 它不如其他答案中的 WhereIf 擴展靈活,但使用起來更短。
public static IQueryable<T1> FilterBy<T1, T2>(this IQueryable<T1> query, T2 expectedValue, Expression<Func<T1, T2>> propertyAccessor)
{
if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor));
if (expectedValue == null) return query;
var equalExpr = Expression.Equal(propertyAccessor.Body, Expression.Constant(expectedValue, typeof(T2)));
var lambda = Expression.Lambda<Func<T1, bool>>(equalExpr, propertyAccessor.Parameters);
return query.Where(lambda);
}
它可以像這樣使用:
var query = dataContext.Staffs.FilterBy(name, s => s.Name);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.