【问题标题】:Linq query causes null reference exception when accessing linked tableslinq查询访问链接表时导致空引用异常
【发布时间】:2020-04-15 08:43:18
【问题描述】:

所以我有一个测试博客的以下 linq 查询:

var random = new Random();
var random = context.Blog.OrderBy(x => random.Next())
                         .Where(x => x.DisplayStatus == "Approved")
                         .Select(x => new Blog 
                          { 
                              Title = x.Title,
                              Content = x.Content,
                              AuthorId = x.AuthorId,
                              Author = x.Authors.AuthorName //null pointer exception here
                          }).Take(5).ToList() 

问题是,当它命中“x.Authors.AuthorName”时会抛出空指针异常。我不知道为什么。我知道作者在那里,因为当我使用 linq 表达式(来自 context.Blog 中的 x 等)时它工作正常,并且它在 LinqPad 中工作。不过,我不能使用 linq 表达式,因为我不知道如何在不使用 lambda 表达式的情况下声明“OrderBy(x => random.Next())”。

这是没有随机的版本

var working = (from x in context.Blog
                 //NO known code to select random
                 where x.DisplayStatus == "Approved"
                 select new Blog 
                 {
                    Title = x.Title,
                    Content = x.Content,
                    AuthorId = x.AuthorId,
                    Author = x.Authors.AuthorName //NO null pointer exception
                 }).Take(5).ToList() 

Blog 是一个没有数据库关系的 POCO 类。 Authors 是一个数据库实体类, AuthorName 是一个简单的字符串。 知道这里发生了什么吗?

【问题讨论】:

  • x.DisplayStaus 中的错字证明它不是您声称有效的复制/粘贴代码。 “每个人都在撒谎” (c) 豪斯博士。所以我们忽略了这个说法。当你获得 NRE 时,xx.Authors 有什么价值?
  • 您可以尝试将第二个变量的名称更改为与分配给 new Random() 的第一个变量不同的名称吗?
  • 我不想实际复制粘贴我的代码,因为完整的 linq 查询相当长。我整理了一个更简洁地描述问题的示例。如果这让您感到困扰,我会修复它。
  • 这能回答你的问题吗? Linq to Entities, random order

标签: c# .net linq


【解决方案1】:

我不允许添加评论,所以我在这里问。 Authors 是什么类型?从名字我猜它可能是某种类型的Collection

如果一个属性可能是null,我通常会添加一个?

Author = x.Authors?.AuthorName

【讨论】:

  • Authors 是一个数据库实体类型类。 AuthorName 是一个简单的字符串
【解决方案2】:

你可以使用Guid.NewGuid()获得随机排序:

var random = context.Blog.OrderBy(x => Guid.NewGuid())
                     .Where(x => x.DisplayStatus == "Approved")
                     .Select(x => new Blog 
                      { 
                          Title = x.Title,
                          Content = x.Content,
                          AuthorId = x.AuthorId,
                          Author = x.Authors.AuthorName
                      }).Take(5).ToList() 

SqlFunctions.Rand:

var random = context.Blog.OrderBy(x => SqlFunctions.Rand())
                     .Where(x => x.DisplayStatus == "Approved")
                     .Select(x => new Blog 
                      { 
                          Title = x.Title,
                          Content = x.Content,
                          AuthorId = x.AuthorId,
                          Author = x.Authors.AuthorName
                      }).Take(5).ToList() 

【讨论】:

  • 两者均产生:“NotSupportedException:此函数只能从 LINQ to Entities 调用。”博客不是实体。我应该提到这一点。
【解决方案3】:

将您的查询更改为:

var working = (from x in context.Blog
                 where x.DisplayStaus == "Approved"
                 select new Blog 
                 {
                    Title = x.Title,
                    Content = x.Content,
                    AuthorId = x.AuthorId,
                    Author = x.Authors.AuthorName
                 })
                 .AsEnumerable().OrderBy(x => random.Next())
                 .Take(5).ToList();

编辑:

我不是 100% 确定错误是什么,我想等待 OP 检查我的答案是否确实符合我的预期。

我假设 Entity Framework 不会将您的查询转换为 SQL 并因此失去自动加载 x.Authors 的能力,通过移动查询的前“一半”OrderBy,可以将 @ 转换为 SQL 987654325@ 将在内存中运行。

所以AsEnumerable() 将强制查询在SQL 中翻译和执行,而下面的OrderBy 将在内存中运行。

编辑2:

关于如何在 SQL Server 上做到这一点,而不是将整个查询加载到内存中,恐怕我帮不了你,谷歌搜索带来了这个

Linq to Entities, random order

【讨论】:

  • 这似乎是解决方案。诀窍似乎是将 .AsEnumerable().OrderBy(x => random.Next()) 放在最后。我尝试将 .AsEnumerable().OrderBy(x => random.Next()) 放在(从 x in ...)之前,但这只会让事情变得更糟。
  • 您的解决方案将整个表从数据库加载到内存中,对其进行排序并获取 5 条记录。
  • 那么合适的解决方案是什么?
猜你喜欢
  • 2011-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多