日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

[译]如何在C#中调试LINQ查询

發(fā)布時間:2023/12/4 C# 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [译]如何在C#中调试LINQ查询 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

LINQ是我在C#中最喜歡的功能之一。它讓代碼看起來更漂亮美觀。我們得到了一個易于編寫和理解的簡潔函數(shù)式語法。好吧,至少我們可以使用LINQ方法的語法風(fēng)格。

LINQ很難進(jìn)行調(diào)試。我們無法知道該查詢內(nèi)部發(fā)生了什么。我們可以看到輸入和輸出,但這就是它的全部。出現(xiàn)問題時會發(fā)生什么?我們只是盯著代碼,試圖獲得某種洞察力?必須有一個更好的方式……

調(diào)試LINQ

雖然很難,但可以使用一些技術(shù)來調(diào)試LINQ。

首先,我們創(chuàng)建一個小場景。假設(shè)我們想要一份按年齡排序的3名男性員工的名單,這些員工的薪水高于平均水平。這是一個非常常見的查詢類型,對吧?這是我為此編寫的代碼:

public IEnumerable<Employee> MyQuery(List<Employee> employees) { var avgSalary = employees.Select(e=>e.Salary).Average(); return employees .Where(e => e.Gender == "Male") .Take(3) .Where(e => e.Salary > avgSalary) .OrderBy(e => e.Age); }

數(shù)據(jù)集為:

姓名年齡性別收入
Peter Claus40“Male”61000
Jose Mond35"male"62000
Helen Gant38"Female"38000
Jo Parker42"Male"52000
Alex Mueller22"Male"39000
Abbi Black53"female"56000
Mike Mockson51"Male"82000

當(dāng)運行此查詢時,我得到的結(jié)果為: PeterClaus,61000,40

這似乎不對…… 應(yīng)改有3名員工的。而平均工資約為56400,因此結(jié)果中應(yīng)包括薪水為62000的“Jose Mond”和薪水為82000的“Mike Mockson”。

所以,我的LINQ查詢中有一個錯我,該怎么辦呢?好吧,我可以盯著代碼,直到我弄明白,這甚至可能適用于這種特殊情況。或者,我可以以某種方式調(diào)試它。讓我們看看如何調(diào)試它。

1. 在快速監(jiān)視中評估查詢的各個部分

你可以做的最簡單的事情之一就是在快速監(jiān)視中分析各個查詢。你可以從第一個操作開始,然后繼續(xù)第一個和第二個操作,以此類推。

這里有一個例子:

你可以使用OzCode的顯示功能來顯示你感興趣的字段,這樣可以輕松找到問題。

我們可以看到即使在第一次查詢之后,就出現(xiàn)了問題。“Jose Mond” 一個男性,貌似沒有查詢到。現(xiàn)在,我可以盯著一小段代碼找出錯誤。我想我明白了,Jose的性別寫成了“male”,而不是“Male”。 我現(xiàn)在可以對查詢做一個小的修復(fù):

  • var res = employees

  • .Where(e => e.Gender.ToLower() == "male") // added "ToLower()"

  • .Take(3)

  • .Where(e => e.Salary > avgSalary)

  • .OrderBy(e => e.Age);

  • 修復(fù)后,執(zhí)行代碼得到結(jié)果為:

  • Jose Mond, 62000, 35

  • Peter Claus, 61000, 40

  • 現(xiàn)在包括了Jose,所以修復(fù)了第一個錯誤。還有另一個錯誤,“Mike Mockson”仍然缺失,我們將用下一個技術(shù)解決。 這種技術(shù)有其缺點。如果你需要在大集合中查找特定項目,則可能需要在快速監(jiān)視窗口中話費大量時間。

    另請注意,某些查詢可以更改應(yīng)用程序狀態(tài)。例如,你可以在lambda函數(shù)中調(diào)用一個可以改變瞬時值的方法,像 varres=source.Select(x=>x.Age++) 。通過在快速監(jiān)視窗口運行,將改變應(yīng)用程序狀態(tài)并危及調(diào)試會話。通過在表達(dá)式中添加 ,nse 無副作用后綴(no-side-effects postfix )避免這種情況。要使用它,首先將表達(dá)式復(fù)制到剪貼板,打開一個空的快速監(jiān)視窗口,然后使用 ,nse后綴手動粘貼表達(dá)式。

    2. 將斷點放入lambda表達(dá)式中

    另一個調(diào)試LINQ的好方法是在lambda表達(dá)式中放置一個斷點。這允許評估單個項目。對應(yīng)大型集合,你可以將其與條件斷點功能結(jié)合使用。 在我們的例子中,我們發(fā)現(xiàn)“Mike Mockson”不是第一個Where操作結(jié)果的一部分。你可以在 .Where(e=>e.Gender=="Male")lambda表達(dá)式中放置條件斷點,條件為: e.Name=="Mike Mockson

    運行查詢后,我們將看到:

    只打印了3個名字,那是因為我們的查詢條件中有 .Take(3),在前3次匹配后停止評估。我們確實想要一份按年齡排序的3名男性員工的名單,這些員工薪水高于平均水平。所以我們可能應(yīng)該在檢查薪水后才使用 Take運算符。將查詢改為一下內(nèi)容:

  • var res = employees

  • .Where(e => e.Gender.ToLower() == "male")

  • .Where(e => e.Salary > avgSalary)

  • .Take(3)

  • .OrderBy(e => e.Age);

  • 正確的結(jié)果是:Jose MondPeter ClausMike Mockson

    在LINQ to SQL中,這種技術(shù)不起作用。

    3. 使用日志中間件方法

    讓我們回到錯誤尚未修復(fù)的初始狀態(tài),面對看似正確的查詢,我們都傻眼了。

    調(diào)試查詢的另一個方法是使用以下擴(kuò)展方法:

  • public static IEnumerable<T> LogLINQ<T>(this IEnumerable<T> enumerable, string logName, Func<T, string> printMethod)

  • {

  • #if DEBUG

  • int count = 0;

  • foreach (var item in enumerable)

  • {

  • if (printMethod != null)

  • {

  • Debug.WriteLine($"{logName}|item {count} = {printMethod(item)}");

  • }

  • count++;

  • yield return item;

  • }

  • Debug.WriteLine($"{logName}|count = {count}");

  • #else

  • return enumerable;

  • #endif

  • }

  • 以下是如何使用它:

  • var res = employees

  • .LogLINQ("source", e=>e.Name)

  • .Where(e => e.Gender == "Male")

  • .LogLINQ("logWhere", e=>e.Name)

  • .Take(3)

  • .LogLINQ("logTake", e=>e.Name)

  • .Where(e => e.Salary > avgSalary)

  • .LogLINQ("logWhere2", e=>e.Name)

  • .OrderBy(e => e.Age);

  • 輸出為:

    說明和解釋:

    • 在LINQ查詢中的每個操作之后放置?LogLINQ方法。它可以選擇打印通過此操作的所有項目和總數(shù)。

    • logName是每個輸出的前綴,可以輕松查看編寫它的查詢步驟。我喜歡將其命名為之后操作相同的名稱。

    • Fun<T,string>printMethod允許打印給定項目的任何內(nèi)容。在上面的示例中,我選擇使用?e=>e.Name打印員工的姓名,當(dāng)為?null時,除總數(shù)外,不會打印任何內(nèi)容。

    • 為了優(yōu)化,此方法盡在調(diào)試模式下有效(?#if DEBUG)。在發(fā)布模式下,它什么都不做。

    • 每個項目都按順序打印,無需等待操作結(jié)束,這是因為LINQ的?lazy?特性。以下是查看單個操作結(jié)果的提示:將整個輸出復(fù)制到?notepad++。然后使用Ctrl+Shift+F(Find)并查找日志前綴(例如?logWhere2)。在查找對話框,點擊Find All in Current Document。這將僅顯示與日志名稱前綴匹配的行。

    查看輸出窗口,可以看到以下幾點:

  • 源中包括“Jose Mond”,但?logWhere沒有,這是因為我們之前看到的區(qū)分大小寫的錯誤。

  • 由于提前使用?Take方法,“Mike Mockson”從未在源中進(jìn)行評估。事實上,源的計數(shù)日志完全丟失,因為它永遠(yuǎn)不會到達(dá)集合的末尾。

  • 對應(yīng) LINQ to SQL以及可能的其他LINQ程序,此技術(shù)存在問題。它將 IQueryable轉(zhuǎn)換為 IEnumerable,更改查詢并可能強(qiáng)制進(jìn)行早期評估。最好不要將它用于任何LINQ程序(如Entity Framework)。

    4. 使用OzCode的LINQ功能

    如果你需要有效工具調(diào)試LINQ,可以使用OzCode Visual Studio擴(kuò)展。

    免責(zé)聲明:我目前是OzCode員工。然而,這是我個人博客,這篇文章只是我的專業(yè)推薦。

    OzCode將可視化你的LINQ查詢,以準(zhǔn)確顯示每個項目的行為方式。首先,它將顯示每次操作后的項目數(shù):

    然后,你可以點擊任何編號按鈕以查看項目以及它們在操作中的進(jìn)度。

    我們可以看到“Jo Parker”在源中排名第4,在第一次 Where操作之后排名第3。它沒有通過第二次的 Where操作。它甚至沒有在最后兩次操作 OrderByTake中處理。

    如果這還不夠,你可以按右上角的“l(fā)ambda”按鈕查看完整的LINQ分析。以下是它的樣子:

    因此,在調(diào)試LINQ方面,你幾乎可以充滿希望和夢想。

    總結(jié)

    調(diào)試LINQ不是很直觀,但可以通過一些技術(shù)很好的完成。

    我沒有提到LINQ查詢語法,因為他沒有被使用太多。只有技術(shù)#2 (lambda斷點)和技術(shù)#4 (OzCode)愛使用了查詢語法。

    我希望你能使用本文的一些技巧,請繼續(xù)關(guān)注以后的帖子。


    總結(jié)

    以上是生活随笔為你收集整理的[译]如何在C#中调试LINQ查询的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。