调试复杂的 Linq 查询

3

我正在处理一个复杂的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();
}

1
你尝试过LINQPad吗? - Luca
@Luca 不,我会看一下的,谢谢! - Cemre Mengü
这个LINQ查询几乎没有什么复杂的东西。 - leppie
3个回答

5
在您的查询中,您可以使用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快捷键),您将看到每个子表达式的结果。

1
请注意查询末尾的.ToList()调用 - 这将覆盖延迟执行,允许您逐步执行代码(更多信息请参见:https://dev59.com/Ronda4cB1Zd3GeqPAo1L#29556281) - Tim Tyler
经常在复杂的Linq查询中忘记使用F11 - Jacques

3
我以前也遇到过同样的问题,下面的技巧帮助了我很多。
我已经将提取的值定义为我的select表达式中的属性,在执行new ObjectName()提取语句之前,这样当发生错误时,Visual Studio将指向导致空引用的特定部分。
考虑以下示例:
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表达式。
尝试使用相同的技术,并告诉我结果。

-1
您可以在new PortChannelData行上设置断点并像往常一样进行调试(每次执行选择语句时都会触发断点,即对于 session.Query<PortChannel>() 中的每个元素)。
如果您将 selectnew PortChannelData 放在同一行,则设置断点将在 Linq 查询的开头处设置断点。该断点仅在查询执行之前 - 只命中一次,并且不允许您“跳转到”查询。但是,您可以通过在Visual Studio中右键单击红色断点圆点并选择“位置...”来更改断点的位置。将位置设置为new PortChannelData的起始行和字符,断点将被放置在 select 语句中。
您还可以向断点添加条件,以便仅在满足条件时激活它们。如果session.Query<PortChannel>()返回大量端口通道,则检查您访问的任何属性或字段是否为空的条件可以节省一些时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接