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

640?wx_fmt=jpegLINQ是我在C#中最喜欢的功能之一。它让代码看起来更漂亮美观。我们得到了一个易于编写和理解的简洁函数式语法。好吧,至少我们可以使用LINQ方法的语法风格。

LINQ很难进行调试。我们无法知道该查询内部发生了什么。我们可以看到输入和输出,但这就是它的全部。出现问题时会发生什么?我们只是盯着代码,试图获得某种洞察力?必须有一个更好的方式……

调试LINQ

虽然很难,但可以使用一些技术来调试LINQ。

首先,我们创建一个小场景。假设我们想要一份按年龄排序的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);	
}

数据集为:

姓名年龄性别收入
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

当运行此查询时,我得到的结果为: PeterClaus,61000,40

这似乎不对…… 应改有3名员工的。而平均工资约为56400,因此结果中应包括薪水为62000的“Jose Mond”和薪水为82000的“Mike Mockson”。

所以,我的LINQ查询中有一个错我,该怎么办呢?好吧,我可以盯着代码,直到我弄明白,这甚至可能适用于这种特殊情况。或者,我可以以某种方式调试它。让我们看看如何调试它。

1. 在快速监视中评估查询的各个部分

你可以做的最简单的事情之一就是在快速监视中分析各个查询。你可以从第一个操作开始,然后继续第一个和第二个操作,以此类推。

这里有一个例子:

640?wx_fmt=png

你可以使用OzCode的显示功能来显示你感兴趣的字段,这样可以轻松找到问题。

我们可以看到即使在第一次查询之后,就出现了问题。“Jose Mond” 一个男性,貌似没有查询到。现在,我可以盯着一小段代码找出错误。我想我明白了,Jose的性别写成了“male”,而不是“Male”。 我现在可以对查询做一个小的修复:

  1. var res = employees

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

  3. .Take(3)

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

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

修复后,执行代码得到结果为:

  1. Jose Mond, 62000, 35

  2. Peter Claus, 61000, 40

现在包括了Jose,所以修复了第一个错误。还有另一个错误,“Mike Mockson”仍然缺失,我们将用下一个技术解决。 这种技术有其缺点。如果你需要在大集合中查找特定项目,则可能需要在快速监视窗口中话费大量时间。

另请注意,某些查询可以更改应用程序状态。例如,你可以在lambda函数中调用一个可以改变瞬时值的方法,像 varres=source.Select(x=>x.Age++) 。通过在快速监视窗口运行,将改变应用程序状态并危及调试会话。通过在表达式中添加 ,nse 无副作用后缀(no-side-effects postfix )避免这种情况。要使用它,首先将表达式复制到剪贴板,打开一个空的快速监视窗口,然后使用 ,nse后缀手动粘贴表达式。

640?wx_fmt=png

2. 将断点放入lambda表达式中

另一个调试LINQ的好方法是在lambda表达式中放置一个断点。这允许评估单个项目。对应大型集合,你可以将其与条件断点功能结合使用。 在我们的例子中,我们发现“Mike Mockson”不是第一个Where操作结果的一部分。你可以在 .Where(e=>e.Gender=="Male")lambda表达式中放置条件断点,条件为: e.Name=="Mike Mockson

640?wx_fmt=png

运行查询后,我们将看到:

640?wx_fmt=png

只打印了3个名字,那是因为我们的查询条件中有 .Take(3),在前3次匹配后停止评估。我们确实想要一份按年龄排序的3名男性员工的名单,这些员工薪水高于平均水平。所以我们可能应该在检查薪水后才使用 Take运算符。将查询改为一下内容:

  1. var res = employees

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

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

  4. .Take(3)

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

正确的结果是:Jose MondPeter ClausMike Mockson

在LINQ to SQL中,这种技术不起作用。

3. 使用日志中间件方法

让我们回到错误尚未修复的初始状态,面对看似正确的查询,我们都傻眼了。

调试查询的另一个方法是使用以下扩展方法:

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

  2. {

  3. #if DEBUG

  4. int count = 0;

  5. foreach (var item in enumerable)

  6. {

  7. if (printMethod != null)

  8. {

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

  10. }

  11. count++;

  12. yield return item;

  13. }

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

  15. #else

  16. return enumerable;

  17. #endif

  18. }

以下是如何使用它:

  1. var res = employees

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

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

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

  5. .Take(3)

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

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

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

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

输出为:

640?wx_fmt=png

说明和解释:

  • 在LINQ查询中的每个操作之后放置 LogLINQ方法。它可以选择打印通过此操作的所有项目和总数。

  • logName是每个输出的前缀,可以轻松查看编写它的查询步骤。我喜欢将其命名为之后操作相同的名称。

  • Fun<T,string>printMethod允许打印给定项目的任何内容。在上面的示例中,我选择使用 e=>e.Name打印员工的姓名,当为 null时,除总数外,不会打印任何内容。

  • 为了优化,此方法尽在调试模式下有效( #if DEBUG)。在发布模式下,它什么都不做。

  • 每个项目都按顺序打印,无需等待操作结束,这是因为LINQ的 lazy 特性。以下是查看单个操作结果的提示:将整个输出复制到 notepad++。然后使用Ctrl+Shift+F(Find)并查找日志前缀(例如 logWhere2)。在查找对话框,点击Find All in Current Document。这将仅显示与日志名称前缀匹配的行。

查看输出窗口,可以看到以下几点:

  1. 源中包括“Jose Mond”,但 logWhere没有,这是因为我们之前看到的区分大小写的错误。

  2. 由于提前使用 Take方法,“Mike Mockson”从未在源中进行评估。事实上,源的计数日志完全丢失,因为它永远不会到达集合的末尾。

对应 LINQ to SQL以及可能的其他LINQ程序,此技术存在问题。它将 IQueryable转换为 IEnumerable,更改查询并可能强制进行早期评估。最好不要将它用于任何LINQ程序(如Entity Framework)。

4. 使用OzCode的LINQ功能

如果你需要有效工具调试LINQ,可以使用OzCode Visual Studio扩展。

免责声明:我目前是OzCode员工。然而,这是我个人博客,这篇文章只是我的专业推荐。

OzCode将可视化你的LINQ查询,以准确显示每个项目的行为方式。首先,它将显示每次操作后的项目数:

640?wx_fmt=png

然后,你可以点击任何编号按钮以查看项目以及它们在操作中的进度。

640?wx_fmt=png

我们可以看到“Jo Parker”在源中排名第4,在第一次 Where操作之后排名第3。它没有通过第二次的 Where操作。它甚至没有在最后两次操作 OrderByTake中处理。

如果这还不够,你可以按右上角的“lambda”按钮查看完整的LINQ分析。以下是它的样子:

640?wx_fmt=png

因此,在调试LINQ方面,你几乎可以充满希望和梦想。

总结

调试LINQ不是很直观,但可以通过一些技术很好的完成。

我没有提到LINQ查询语法,因为他没有被使用太多。只有技术#2 (lambda断点)和技术#4 (OzCode)爱使用了查询语法。

我希望你能使用本文的一些技巧,请继续关注以后的帖子。

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/315500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CodeForces - 375D Tree and Queries 树启 + 思维

传送门 题意&#xff1a; 思路&#xff1a; 很明显子树问题会想到树启&#xff0c;让后如何updateupdateupdate呢&#xff1f;一个显然的思路就是维护一个树状数组&#xff0c;查询次数>kj>k_j>kj​的个数。但是这样复杂度是O(nlog2n)O(nlog^2n)O(nlog2n)的&#xf…

WebSocket数据加密——AES与RSA混合加密

前言之前在写“一套简单的web即时通讯”&#xff0c;写到第三版的时候没什么思路&#xff0c;正好微信公众号看到一篇讲API交互加密&#xff0c;于是就自己搞了一套AES与RSA混合加密&#xff0c;无意中产生应用在WebSocket想法&#xff0c;好在思路都差不多&#xff0c;稍微改动…

基于 EntityFramework 生成 Repository 模式代码

借助 WeihanLi.EntityFramework 实现简单的 RepositoryIntro很多时候一些简单的业务都是简单的增删改查&#xff0c;动态生成一些代码完成基本的增删改查&#xff0c;而这些增删改查代码大多类似&#xff0c;只有一些有复杂业务逻辑的可能需要手动去写。于是实现了一个基于 EF …

P3168 [CQOI2015]任务查询系统 主席树 + 差分

传送门 题意&#xff1a; 思路&#xff1a; 题目中(si,ei,pi)(s_i,e_i,p_i)(si​,ei​,pi​)转换成操作即为在[si,ei][s_i,e_i][si​,ei​]区间内加上pip_ipi​的优先级&#xff0c;让后查询的话就是查询第xix_ixi​秒优先级最小的kik_iki​个任务的优先级之和。可知这两个操…

asp.net core 系列之Performance的 Response compression(响应压缩)

本文&#xff0c;帮助了解响应压缩的一些知识及用法(大部分翻译于官网,英文水平有限,不准确之处,欢迎指正)。什么是响应压缩&#xff1f;响应压缩简单的说就是为了减少网络带宽&#xff0c;而把返回的响应压缩&#xff0c;使之体积缩小&#xff0c;从而加快响应的一种技术(个人…

Codeforces Round #603 (Div. 2) E. Editor 线段树维护括号序列

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 首先一个括号序列合法的条件可以转化成两个(左括号代价为111&#xff0c;右括号代价为−1-1−1)&#xff1a; (1) 左括号个数等于右括号个数。 (2) 括号的前缀和非负。 所以我们直接用线段…

从ASP.NET Core 3.0 preview 特性,了解CLR的Garbage Collection

前言在阅读这篇文章&#xff1a;Announcing Net Core 3 Preview3的时候&#xff0c;我看到了这样一个特性&#xff1a;Docker and cgroup memory LimitsWe concluded that the primary fix is to set a GC heap maximum significantly lower than the overall memory limit as …

asp.net core使用serilog将日志推送到腾讯云日志服务

为什么是serilog&#xff1f;Serilog是 .NET 中最著名的结构化日志类库。基于日志事件log events&#xff0c;而不是日志消息log message。你可以将日志事件格式化为控制台的可读文本或者可以将相同的事件格式化为JSON并将其发送到远程日志服务器。应用程序中的日志语句会创建L…

P5787 二分图 /【模板】线段树分治

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 线段树分治就是在线段树上进行遍历&#xff0c;到每个点都加上它对子节点的贡献&#xff0c;最后到叶子节点的时候算一下贡献。 对于这个题先考虑维护二分图的话&#xff0c;可以用扩展域并…

微软发布 VS Code Java 安装程序,一键安装所有 Java 开发环境

北京时间 2019 年 6 月 14 日 &#xff0c;微软发布了 VS Code Java 安装程序&#xff0c;方便开发者能一键安装所有 Java 开发环境。几乎是在三年前&#xff0c;在微软苏黎世办公室的编程马拉松中&#xff0c;来自 Red Hat&#xff0c;IBM&#xff0c;Codenvy 和 Microsoft 的…

[译]C#中的条件断点

这只是你们许多人中可能使用的一个简单技巧。这是一个小但非常强大的技巧&#xff0c;在你调试大型代码库时尤其有用。这是条件断点的概念。正如名称本身所暗示的那样&#xff0c;只有在满足某个条件时才会设置被击中的断点。它也很容易实现。创建一个新的应用程序并设置一个普…

前菜---二叉树+堆的小练习

目录 前言&#x1f3dc;️ 1. 二叉树性质总结⛱️ 1.2 性质3⏰ 2. 二叉树性质小练习&#x1f3d5;️ 3. 答案解析&#x1f4a1; 4. 堆概念结构小练习&#x1fa94; 5. 答案解析&#x1f9ff; 6. 前/中/后/层序遍历小练习&#x1f52b; 7. 答案解析&#x1f9fa; 后语…

牛客 CCA的区间 dp + 补集转移

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 首先翻转一个区间意味着可以将任意两段不相交的区间组合&#xff0c;所以问题变成了选两端不相交的区间&#xff0c;使得合并后区间和最大。那么我们就处理出来区间&#xff0c;让后进行转…

ASP.NET Core IP 请求频率限制

在网站或API应用中&#xff0c;我们为了防止无聊人士或恶意攻击&#xff0c;通常希望屏蔽某一IP短时间的内高频率请求。在ASP.NET Core中&#xff0c;限制IP请求频率非常简单&#xff0c;我们来看看吧。轮子一个.NET Core 目前的生态发展十分迅猛&#xff0c;轮子也越来越多。只…

Exceptionless - .Net Core开源日志框架

作者&#xff1a;markjiang7m2原文地址&#xff1a;https://www.cnblogs.com/markjiang7m2/p/11020140.html官网地址&#xff1a;http://letyouknow.net今天要给大家介绍的Exceptionless是一个基于 .net core的开源日志框架&#xff0c;Exceptionless的意思是&#xff1a;没有异…

CF981E Addition on Segments 线段树分治 + bitset优化

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 我们考虑如果我们选择的区间都包含某个位置&#xff0c;那么这个位置一定是最大值。那么对于每个位置&#xff0c;我们枚举包含其的区间&#xff0c;让后每次加xxx都用bitsetbitsetbitset来…

开源基于Canal的开源增量数据订阅消费中间件

canal 是阿里巴巴开源的一款基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB)。我开发的这个CanalSync项目 https://github.com/yuzd/CanalSync >觉得不错帮忙给个star谢谢是基于canal-server之上的数据库同步&消费中间件&#…

不装 VS 自己编译安装 Windows Terminal

Windows Terminal 一直没有发布可以直接安装的二进制文件&#xff0c;想自行编译的时候&#xff0c;看到系统需求中包含体积巨大的 Visual Studio 和 Windows SDK&#xff0c;脑袋都大了。直接下载其他人编译好的安装包又不放心&#xff0c;那么就想个办法避免在本地环境编译吧…

带你了解C#每个版本新特性

上学时学习C#和.NET&#xff0c;当时网上的资源不像现在这样丰富&#xff0c;所以去电脑城买了张盗版的VS2005的光盘&#xff0c;安装时才发现是VS2003&#xff0c;当时有一种被坑的感觉&#xff0c;但也正是如此&#xff0c;让我有了一个完整的.NET的学习生涯。一直都认为学习…

从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)

默认情况下&#xff0c;我们打包 NuGet 包时&#xff0c;目标项目安装我们的 NuGet 包会引用我们生成的库文件&#xff08;dll&#xff09;。除此之外&#xff0c;我们也可以专门做 NuGet 工具包&#xff0c;还可以做 NuGet 源代码包。然而做源代码包可能是其中最困难的一种了&…