LINQ查询有时(随机地,对于完全相同的数据)抛出NullReferenceException

时间:2016-06-09 07:22:18

标签: c# entity-framework linq linq-to-entities

我正在使用LINQ-to-Entities(EF 6.1.3)来执行以下查询:

var users = msgList.Select(m => m.From)
                    .Union(msgList.Select(m => m.To))
                    .Distinct()
                    .Where(u => u.ID != userId) //userId is an assigned local var.
                    .ToList();

msgList是一个列表(已经提取,可查询且延迟加载已关闭)Message s由一些字段组成例如保证为非空FromTo。原始查询中FromTo都是Include d,因此保证它们为非空。

我的User对象也保证不为空,所以没有什么可以实际为空。

然而,这行有时抛出空指针异常,有时用完全相同的用户完全,完全相同的数据库,完全相同的数据(没有改变) 。加载不是问题,因为它是尚未投入生产的代码,而且我是唯一一个测试它的代码。

Where电话似乎抛出了异常:

at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext() 
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

这怎么可能发生?

更新:这当然是重复What is a NullReferenceException, and how do I fix it?。任何理智的开发人员,即使对.NET / C#/ OOP有一点了解也知道这个错误是什么,这个问题与它无关,即使它将异常作为其中的一部分。

更新2:我已将其切换为每行分配一个列表,如下所示:

var msgListSelection = msgList.Select(m => m.From).ToList();
var union = msgListSelection.Union(msgList.Select(m => m.To)).ToList();
var distinct = union.Distinct().ToList();
var where = distinct.Where(u => u.ID != userId).ToList();
var users = where;

异常发生在where行:

var where = distinct.Where(u => u.ID != User.ID).ToList();

如果distinct返回null,则会在上面一行的ToList var distinct = union.Distinct().ToList();调用中抛出。

我错过了什么吗?

更新2:我的User类是映射到数据库中实体类型的POCO C#类型,其ID属性为long,并且我的Message类再次是在实体框架中映射的POCO类型,导航属性FromTo保证为某些User实例非空。它们被注释为Required,我还在数据库级别检查了它们,以确定。

更新3:我的EF上下文从请求的开头(在请求开头的委托处理程序中设置)到结束。我不认为问题与DbContext的生命周期有关,因为有许多控制器具有相同的机制,有数十种方法可以访问上下文,而我只有这样的问题。这个特殊的方法。

更新4:我已对区别添加了空检查:

var distinct = union.Distinct().ToList();
if(distinct == null)
{
    throw new Exception("distinct was null");
}
var where = distinct.Where(u => u.ID != userId).ToList();

似乎没有问题地传递了这一点,但是在最后一行var where = distinct.Where(u => u.ID != userId).ToList();抛出空指针异常,这排除了distinct可能为空的可能性。

更新5 :我已经编写了一个API测试工具,并向同一个用户发送了大约250个请求到同一个端点。第一个失败了这个错误,其余的都成功了。第一个请求似乎有问题。

1 个答案:

答案 0 :(得分:2)

您可能遇到closure principle导致的问题。您在LINQ查询中引用User属性。因为LINQ查询本身是作为(匿名)方法委托执行的,所以闭包原则适用。

引用上述链接:

  

本质上,闭包是一个可以在a处执行的代码块   以后的时间,但它保持了它的第一个环境   创建 - 即它仍然可以使用方法的局部变量等   即使在该方法完成执行后,也会创建它。

User属性的使用受此原则的约束。在执行LINQ查询时,它的值可能已更改。为防止这种情况,应将User属性复制到LINQ查询中引用的局部变量。像这样:

var user = User;
var users = msgList.Select(m => m.From)
                   .Union(msgList.Select(m => m.To))
                   .Distinct()
                   .Where(u => u.ID != user.ID)
                   .ToList();

<强>更新

当使用user属性的本地参考副本时,NullReferenceException的另一种可能性可能在于Select-Union-Distinct方法。调用ToList时,Where子句将对两个Select子句的并集中的所有项执行。默认情况下,Distinct executes the Equals method from the IQuality interface将在Select(m => m.From)的元素上调用。如果此元素为null,则会导致NullReferenceException