为了账号安全,请及时绑定邮箱和手机立即绑定

为什么我没有收到空引用异常?

为什么我没有收到空引用异常?

C#
Cats萌萌 2022-10-15 15:11:23
我正在使用 LINQ to Entities 从数据库中获取一些数据。以下是我的查询。var location = from l in dbContext.Locationsjoin e in dbContext.Equipment on l.ID equals e.LocationID into rs1from e in rs1.DefaultIfEmpty()where ids.Contains(l.ID)select new{    EquipmentClass = e,    LocationID = l.ID,    LocationName = l.Name,    EquipmentName = e == null ? null : e.Name,    Description = e == null ? null : e.Description,    InServiceStatus = e == null ? false : e.InServiceStatus,    EquipmentType = e.EquipmentType.Name};foreach (var item in location){    // some logic}在上面的代码中,ids是我传入以过滤结果的整数列表。当我得到结果时,我看到其中一个返回记录有一个 null EquipmentClass。我执行了一些空值检查,但意识到我忘记对其中一个属性进行空值检查。现在我希望得到一个空引用异常,EquipmentType = e.EquipmentType.Name但我没有。令我惊讶的是,它工作得很好,并且设置为 null。MyEquipmentClass的属性类型为EquipmentType,这是另一个类。EquipmentType有一个Name属性,它是一个字符串。为什么这不会引发空引用异常?就像测试一样,我从中删除了空检查,InServiceStatus = e == null ? false : e.InServiceStatus并在使用查询运行 foreach 循环时失败并出现无效操作异常。这是否意味着我只需要对不可为空的值进行空检查?
查看完整描述

3 回答

?
MYYA

TA贡献1868条经验 获得超4个赞

如果您针对 编写 LINQ 查询IQueryable,会发生什么情况是您在后台调用的方法(例如SelectWhere等)除了记录您如何调用它们之外没有做任何其他事情,即它们记录谓词表达式并继承一个 LINQ 提供程序。一旦您开始迭代查询,就会要求提供者执行查询模型。所以基本上,提供者使用表达式模型为您提供预期类型的结果。

提供者绝不需要实际编译甚至执行您作为表达式交付的代码(模型)。事实上,LINQ to SQL 或 LINQ to Entities 的全部意义在于提供程序不这样做,而是将代码表达式转换为 SQL。

因此,您的查询实际上被呈现为 SQL 查询,并且该查询的结果被翻译回来。因此,您在查询中看到的变量e不一定是真正创建的,而只是用于 LINQ 提供程序来编译 SQL 查询。但是,大多数数据库服务器都有空传播。

对 LINQ to Objects 运行相同的查询,您将得到缺少的 NullReferenceException


查看完整回答
反对 回复 2022-10-15
?
慕尼黑5688855

TA贡献1848条经验 获得超2个赞

您的更新帮助我了解了您真正关心的问题。关于 LINQ 查询,您需要了解的概念是 LINQ 中的延迟执行

请通过以下链接了解更多详情:

LINQ 中的延迟执行有什么好处?

Linq - 找出延迟执行的最快方法是什么?

现在你的情况会怎样?您已将查询存储在location变量中。该特定步骤只是初始化部分。它并没有真正通过您的 ORM 层对您的数据库执行查询。这就是您可以测试的方式。

location在使用 LINQ 查询初始化变量的代码行上放置一个断点。当调试器在 Visual Studio 中停止时,请转到 SQL Server Management Studio (SSMS) 并启动 SQL Server Profiler 会话。

现在在 Visual Studio 中按下F10以跳过代码语句。此时,您将在分析器会话中看到绝对没有查询执行,如下所示:

//img1.sycdn.imooc.com//634a5d4300017fae12140406.jpg

这都是因为 LINQ 查询直到此时才被执行。


现在您可以访问以下代码行:


foreach (var item in location)

{

    var p = item.EquipmentClass.EquipmentType.Name;

}

在您进入 foreach 循环的那一刻,LINQ 查询被触发,您将在 SQL Server 探查器中看到相应的登录跟踪会话。因此,除非枚举 LINQ 查询,否则它不会被触发。这称为延迟执行,即运行时延迟执行直到枚举。如果您从未location在代码中枚举变量,那么查询执行将永远不会发生。


因此,要回答您的查询,只有在查询被触发时才会出现异常。不是在那之前!


更新 1:您是说 -我没有得到空引用异常。是的!在您到达缺少相应 RHS 加入记录的记录之前,您不会得到空引用异常。请查看以下代码以更好地理解:


class Program

{

    static void Main(string[] args)

    {

        var mylist1 = new List<MyClass1>();

        mylist1.Add(new MyClass1 { id = 1, Name1 = "1" });

        mylist1.Add(new MyClass1 { id = 2, Name1 = "2" });


        var mylist2 = new List<MyClass2>();

        mylist2.Add(new MyClass2 { id = 1, Name2 = "1" });


        var location = from l in mylist1

                       join e in mylist2 on l.id equals e.id into rs1

                       from e in rs1.DefaultIfEmpty()

                       //where ids.Contains(l.ID)

                       select new

                       {

                           EquipmentClass = e,

                           InServiceStatus = e == null ? 1 : e.id,

                           EquipmentType = e.id

                       };


        foreach (var item in location)

        {


        }

    }

}


class MyClass1

{

    public int id { get; set; }

    public string Name1 { get; set; }

}


class MyClass2

{

    public int id { get; set; }


    public string Name2 { get; set; }

}

所以,现在当我开始迭代location变量时,它不会在第一次迭代中中断。它在第二次迭代中中断。当它无法获得与2 中存在的MyClass1对象对应的记录/对象时,它会中断。2没有任何对象。希望这会有所帮助!idmylist1mylist2id


查看完整回答
反对 回复 2022-10-15
?
慕仙森

TA贡献1827条经验 获得超8个赞

你的e.EquipmentType.Nameisnull并且它被分配给它并且分配给一个类型EquipmentType是非常好的,你正在使用它,如果它不匹配任何条件,它将使用它们的默认值初始化元素,在这种情况下,你被设置为哪个很好我猜。使用它会抛出异常。nullnullableDefaultIfEmpty()e.ElementType.NamenullToList()

我希望我说得有道理,你们可能已经讨论过这个问题。


查看完整回答
反对 回复 2022-10-15
  • 3 回答
  • 0 关注
  • 107 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信