我正在使用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由一些字段组成例如保证为非空的From
和To
。原始查询中From
和To
都是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类型,导航属性From
和To
保证为某些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个请求到同一个端点。第一个失败了这个错误,其余的都成功了。第一个请求似乎有问题。
答案 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
。