调试复杂的 linq 查询

本文关键字:查询 linq 复杂 调试 | 更新日期: 2023-09-27 18:32:43

我正在处理一个复杂的linq查询,如下所示,它在运行时引发System.NullReferenceException。不幸的是,Visual Studio 不提供任何其他信息,我无法对值进行QuickWatch以检查哪个值为空。有没有办法调试这种查询,以便我可以看到出了什么问题?

var dataSource = (from c in session.Query<PortChannel>()
        select
            new PortChannelData
            {
                Location = c.Port.MagazineSlot.SiteMagazine.Site.SiteName,
                MagazineName =
                    String.Format("{0}-{1}", c.Port.MagazineSlot.SiteMagazine.MagazineType.MagazineName,
                        c.Port.MagazineSlot.SiteMagazine.MagazineNo),
                SlotNo = c.Port.MagazineSlot.SlotNo,
                PortNo = c.Port.PortNo,
                Klm = c.Klm,
                StmNo = c.StmNo,
                Label = c.SiteName == null ? null : String.Format("{0}_{1}", c.SiteName, c.E1No),
                ChannelFullName = c.ChannelFullName,
                BscRncPort = c.BscRncPort,
                MgwPort = c.MgwPort,
                ReservedBy = c.ReservedBy,
                CrossFullName = c.CrossConnection == null ? null : c.CrossConnection.ChannelFullName
            }).ToList();
}

调试复杂的 linq 查询

在查询中,可以使用 let 关键字来存储子表达式的结果。然后,在创建新的PortChannelData对象时分配结果。

var dataSource = 
    (from c in session.Query<PortChannel>()
        let flName = c.CrossConnection == null ? null : c.CrossConnection.ChannelFullName
        let magName = String.Format("{0}-{1}", c.Port.MagazineSlot.SiteMagazine.MagazineType.MagazineName,
                    c.Port.MagazineSlot.SiteMagazine.MagazineNo)
        // and so on
        select new PortChannelData
        {
            MagazineName = magName,
            CrossFullName = flName,
            // ...
        }).ToList();

这样,当您调试并单步执行查询(F11是标准的 Visual Studio 快捷方式)时,您将看到每个子表达式的结果。

之前遇到过同样的问题,以下技术对我有很大帮助。

我已经将提取的值定义为select表达式中的属性,并且在执行new ObjectName()提取语句之前,这样当错误发生时,Visual Studio 会将我指向导致 null 引用的特定部分。

请考虑以下示例:

var returnNews = reportResult.Select(n =>
            {
                var createdBy = allUsersInfoForReport.FirstOrDefault(u => u.ID == n.CreatedBy);
                var publishedBy = allUsersInfoForReport.FirstOrDefault(u => u.ID == n.Publishedby);
                var modifiedBy = allUsersInfoForReport.FirstOrDefault(u => u.ID == n.LastModifiedBy);
                newsViewCountEntity = newsViewCountCollection.FirstOrDefault(nv => nv.News_ID == n.ID);
                newsCommentsCount = newsCommentsCollection.Count(s => s == n.ID);
                return new ReportItemViewModel()
                {
                    ID = n.ID,
                    AddedBy = createdBy != null ? createdBy.UserName : "",
                    UserSectionName = createdBy != null ? createdBy.RelatedSectionName : "",
                    PublishedBy = publishedBy != null ? publishedBy.UserName : "",
                    LastModifiedBy = modifiedBy != null ? modifiedBy.UserName : "",
                }
});

在此示例中,如果以下任一属性 ( createdBy - publishedBy - modifiedBy ) 无法获取有效值,则会引发异常,但正确突出显示该行,而不是突出显示整个 LINQ 表达式。

尝试遵循相同的技术,并让我知道结果。

可以在new PortChannelData行上放置断点并像往常一样进行调试(每次执行select语句时都会命中断点,即对于session.Query<PortChannel>()中的每个元素)。

如果已将selectnew PortChannelData放在同一行上,则设置断点会在 Linq 查询的开头放置断点。该断点仅在执行查询之前命中一次,并且不允许您"跳入"查询。但是,可以通过右键单击装订线中的红色断点点并选择"位置..."来更改 Visual Studio 中断点的位置。将位置设置为new PortChannelData的起始行和字符,断点将放在 select 语句中。

还可以向断点添加条件,以便仅在满足条件时激活断点。如果session.Query<PortChannel>()返回大量端口通道,则检查您正在访问的任何属性或字段是否为 null 的条件可以为您节省一些时间。