在VS中调试LINQ(Lambda)

来源:https://note.guoqianfan.com/2021/03/21/debug-linq-with-vs/

前言

Linq调试有3种方法,准确来说是2种,因为LinqPad算是复制代码段到外部了。。

  1. VS自带调试:lambda表达式打断点
  2. VS插件OzCode
  3. LinqPad

VS自带调试

在VS里,是可以对Linq调试的,不过一般打断点都会打在整个语句上,这时候我们要换个打法,把断点打在lambda表达式上。

注意和前提

  1. Linq是Linq to object

  2. 对于Linq to object,只有集合对象是 IEnumerable 时,才能命中到Linq里的lambda表达式,IQueryable是不行的。如果是IQueryable,此时就算在lambda表达式里打上断点,在代码执行时,断点会向上转移到整个语句上。

    如果是IQueryable,在lambda表达式里打上断点和设置操作,操作会输出错误:order name: id=error CS0103: 当前上下文中不存在名称“p”, name=error CS0103: 当前上下文中不存在名称“p”

  3. 对于Linq to object,当集合对象是 IEnumerable 时,是延迟执行的。只有结果被用到时,才会进行迭代。所以如果在实际执行前,集合数据发生改变会导致结果集和预期不符。

  4. 对于Linq to object,当集合对象是 IEnumerable 时,对单个对象进行迭代的方式是:先把单个对象走完所有的Linq方法后,直到最后或者执行到返回值不是IEnumerable的Linq方法(该方法会被执行),才会迭代下一个对象。

    如果Linq方法的返回值不是IEnumerable,单个对象的迭代会到该方法(含)为止,会立即进行下一个对象的迭代。所有的对象迭代完毕后,会有一个临时的结果集(非IEnumerable),然后把这个结果集重复前面的步骤,直至结束。

    OrderBy()的返回值是IOrderedEnumerable,所以运行了OrderBy()后,单个对象的迭代就会结束,继续下一个对象的迭代。,然后把这个暂存结果集执行OrderBy()后面的Linq方法。

  5. 4 的基础上,对于IEnumerable,如果有多个条件,我们可以写在同一个Where()里,也可以拆开写在多个Where()里,不会影响效率的,因为不会生成多个暂存结果集。

  6. IEnumerable.AsQueryable().AsEnumerable()是没问题的,遵循IEnumerable的正常流程:断点不会转移,仍然是延迟执行。

操作步骤

有2种方法:

  • 光标移动到箭头=>后面的lambda语句(方法体)内,按F9,这个lambda语句的断点就打上了。其他的lambda语句操作类似。
  • 右键单击其中一个lambda语句(方法体)内的任意位置,然后选择“断点 - >插入断点”。断点就打在这个lambda表达式上了。

上面的2种方法,都是要把位置选在lambda语句内,因为这个语句才是方法体,必须要定位到方法体内才行!否则还是打在外面了!

断点的高级用法

打断点后,我们可以对断点进行设置,可以达到2个目的:

  1. 满足条件才触发断点(条件断点
  2. 触发断点后,输出当前的数据(断点操作

操作步骤

鼠标放在断点的小红点上,会出现浮动块,点击里面的齿轮,我们可以在里面设置条件操作(可以同时勾选设置)。

  • 条件:满足条件才触发断点
  • 操作:触发断点后,输出当前的数据
条件 (条件断点)

勾上条件,会出现设置框,有3个框。
前2个框可以点开看看一些选择项,第3个框可以输入一些代码,代码里可以使用变量/方法,会有智能提示的。

注意:lambda表达式的参数没有提示,需要手动输入参数名和参数的属性/方法

设置好后,只有满足设置的条件,才会触发断点。

操作 (断点操作)

勾上操作,会出现输入框和勾选框。

我们可以在输入框里输入一些字符串,字符串里可以使用变量/有返回值的方法,不过它们必须要放在 {} 里,会有智能提示的。

注意:lambda表达式的参数没有提示,需要手动输入参数名和参数的属性/方法。如果想输出{},需要转义\{;如果想输出\,需要转义\\

另外,还可以使用一些特殊关键字,具体的可以把鼠标放在输入框右侧的 叹号! 上,会有提示的。

接下来说一下勾选框(继续执行),它默认是勾选的:

  • 勾选:当触发断点并输出数据后,程序不会停下来,会继续执行后面的代码;并且断点的小红点会变成菱形
  • 不勾选:当触发断点并输出数据后,程序停下来

设置好后,当断点触发时,会在输出窗口里输出数据的。不过输出窗口里会有其他信息,此时需要我们慢慢找数据了。。。

如果集合是IQueryable,在lambda表达式里打上断点和设置操作,操作会输出错误:order name: id=error CS0103: 当前上下文中不存在名称“p”, name=error CS0103: 当前上下文中不存在名称“p”

注意

  1. 不能调试LINQ to SQL,因为LINQ to SQL是翻译成sql语句了。具体见单步执行和 LINQ

  2. 由于要对单个Linq语句打断点,建议每个Linq语句都放在单独的一行,这样也清晰易读。

    Robot tmpRobot01 = robots
        .Where(p => p.Id == miku001.Id)
        .OrderBy(p => p.Name)
        .FirstOrDefault();
    

疑问

如果Linq里没有lambda表达式,打断点就打在了整个语句上,而不是单个Linq上。是这个原因吗?

参考

  1. 如何在C#中调试LINQ查询:https://michaelscodingspot.com/debug-linq-in-csharp/
  2. C#中的条件断点:https://www.c-sharpcorner.com/UploadFile/b1df45/conditional-breakpoints-in-C-Sharp/
  3. 调试 LINQ:https://docs.microsoft.com/zh-cn/visualstudio/debugger/debugging-linq?view=vs-2019

VS插件OzCode

VS插件OzCode的功能强大,简单易用,可是是收费的。不过OzCode对MVP和开源贡献者是免费的,这就需要努力了。

我没使用过,暂时放几个链接:

  1. 2017年调试LINQ:LINQPad与OzCode:https://oz-code.com/blog/debugging-linq-available-tool-comparison/
  2. 如何在C#中调试LINQ查询:https://michaelscodingspot.com/debug-linq-in-csharp/
  3. Vs 调试插件 —OzCode 特性讲解+破解工具和教程:https://blog.csdn.net/sky__god/article/details/86153982

LinqPad

这个软件很强大,可以执行代码片段,当然也可以执行Linq了。我几乎不用,暂时放几个链接:

  1. 2017年调试LINQ:LINQPad与OzCode:https://oz-code.com/blog/debugging-linq-available-tool-comparison/
  2. 如何在C#中调试LINQ查询:https://michaelscodingspot.com/debug-linq-in-csharp/

扩展

如何知道每一步链式调用的结果

如何知道每一步链式调用的结果?如何知道每一句Linq执行的结果?

有4种方法:

  1. VS里使用【快速监视】
  2. VS里使用断点设置里的【操作】
  3. 使用OzCode
  4. 使用LinqPad

VS里使用【快速监视】

首先在整个语句上设置断点,当程序运行到该断点时,在集合对象上右键->快速监视,然后把想知道结果的整个代码复制到表达式文本框里,点击右侧的重新计算,就能知道这步链式调用的结果了。

注意
  1. 只有把断点设在整个语句上才能监视到。不能设置在lambda表达式上。

    因为lambda表达式是被编译成了一个方法,断点在这个方法里。运行到该断点时,上下文是这个方法的上下文,只能访问到该方法内部变量,是不能访问到外部对象的!

  2. 该方式只能适用于返回结果较少的情况,如果返回结果很多,估计会出问题。某人说:vs没事儿给你抽个风,整个调试器都直接挂,必须重启调试才能继续

图示
vs里使用快速监视

VS里使用断点设置里的【操作】

这种方式里的断点是设置在lambda表达式上,和前面的VS里使用【快速监视】 里的断点位置不一样!!!

把断点设置在lambda表达式上,然后在断点设置里添加条件和操作。

  • 条件必须和lambda表达式一模一样,否则数据就不同了,建议直接把lambda表达式复制进去。
  • 操作里输出有用的简单的信息。

详细的操作步骤见前面的断点的高级用法

不填条件的偷懒法

由于每个断点设置里的条件都要把lambda表达式复制进去,十分麻烦,推荐一个简单的方法:

每个Linq语句的结果让下一个Linq语句输出,下一个Linq语句不要设置条件,只设置操作。(因为只有当前Linq语句满足条件后,才会进入下一个Linq语句。)

不过如果只有一个Linq语句或者是最后一个Linq语句,这种偷懒方式就不行了,这时候我们只有1种选择:再加一个Linq语句(OrderBy...),让它来输出。

其实还有一种选择:在断点里添加条件。不过这种选择只适用于只有一个Linq语句的情况。是最后一个Linq语句时是不行的!因为最后一个Linq语句输出的是上一条Linq语句的信息的,如果添加了条件,输出的就是当前Linq语句的信息了,那上一条Linq语句的信息由谁来输出?

注意
  1. 该方式只能适用于返回结果较少的情况,如果返回结果很多,输出窗口估计能翻好几页吧,那就难受了。。
图示

下图是不填条件偷懒法每个Linq语句的结果让下一个Linq语句输出,下一个Linq语句不要设置条件,只设置操作。所以图中是 OrderBy输出Where的执行结果

vs里使用断点设置里的操作-不填条件偷懒法

使用OzCode

VS插件OzCode很强大,每一个Linq语句的执行结果都能统计并展示出来,详情参考:如何在C#中调试LINQ查询如何在C#中调试LINQ查询

使用LinqPad

LinqPad软件很强大,不过数据源是个问题,操作步骤参考:如何在C#中调试LINQ查询如何在C#中调试LINQ查询

参考

  1. 2017年调试LINQ:LINQPad与OzCode:https://oz-code.com/blog/debugging-linq-available-tool-comparison/
  2. 如何在C#中调试LINQ查询:https://michaelscodingspot.com/debug-linq-in-csharp/
  3. C#中的条件断点:https://www.c-sharpcorner.com/UploadFile/b1df45/conditional-breakpoints-in-C-Sharp/
  4. 调试 LINQ:https://docs.microsoft.com/zh-cn/visualstudio/debugger/debugging-linq?view=vs-2019
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容