在 Entity Framework 6 查询中取消引用可能为空的引用
Posted
技术标签:
【中文标题】在 Entity Framework 6 查询中取消引用可能为空的引用【英文标题】:Dereference of a possibly null reference in Entity Framework 6 query 【发布时间】:2022-01-02 09:27:15 【问题描述】:我有一个启用了可为空引用类型的 .NET 6 项目 (<Nullable>enable</Nullable>
)。我有这个 EF 实体:
public class PostFile
public Int32 UserId get; set;
public Int32 PostId get; set;
public virtual User? User get; set;
public virtual Post? Post get; set;
我在上面添加了?
以防止出现此可为空的警告:
Non-nullable property '...' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
现在,我有这个 Entity Framework 6 LINQ 查询:
var postFiles = context.postFiles.Where(x => x.User.Id == request.UserId);
...但我收到以下警告:
Dereference of a possibly null reference.
...关于我的查询的这一部分:
x.User.Id == ...
如何解决此警告?
【问题讨论】:
PostFile和User的表关系是什么? (用户属性在哪里?)是否保证每个 PostFile 都只有一个用户(并且永远不会“没有”用户)? 另外,您是否希望 PostFile 没有关联的 Post?当您期望该值可能为空时,您只会使用?
。如果没有匹配的 Post,我不希望 PostFile 存在。
PostFile 将始终有一个 User 和 Post 但我相信 EF 在同样的情况下可能不会加载 Post 或 File?
可能是 context.postfiles.Include(x => x.User).Where(....entityframework.net/include
【参考方案1】:
我想你的意思是这样的:
public class PostFile
public Int32 UserId get; set;
public Int32 PostId get; set;
public virtual User? User get; set;
public virtual Post? Post get; set;
您最初的问题是 C#8 引入的警告,它更明确地使用可空引用类型。对于实体,上述实现无效,除非这些关系确实是可选的,这将要求它们的 FK 字段(UserId 和 PostId)也可以为 Null。它们可能不是可选的。
解决这个问题的主要方法:
A) 关闭该功能。 (在项目中禁用可空引用)
B) 请求“宽恕”这些不应该为空,但在构造时不会处于有效状态的事实。 (EF 会管理它们)
public class PostFile
public Int32 UserId get; set;
public Int32 PostId get; set;
public virtual User User get; set; = null!;
public virtual Post Post get; set; = null!;
更改模型以将导航属性标记为可空引用可能会导致各种问题,就像它可以迁移一样,并且将开始用可空的 FK 替换不可空的 FK。将这些引用标记为 Null-able 并让 EF 满意:
public class PostFile
public Int32? UserId get; set;
public Int32? PostId get; set;
public virtual User? User get; set;
public virtual Post? Post get; set;
这几乎肯定不是您在您的域中想要的,如果 UserId 和 PostId 是 PK 的一部分,甚至是合法的。
就我个人而言,我将 C# 中的这种变化称为最初默认启用的“地雷”MS,例如 EF 中的客户端评估。 :) 我预见到有关此警告或重大更改的许多 *** 问题,以及充斥着“!”的许多客户端代码库宽恕标记作为旧的不可空对象/引用被传递到代码中,并带有可空引用检查。
【讨论】:
【参考方案2】:您应该将导航实体标记为可为空。您不应该启用延迟加载,因此导航属性可以从查询返回为null
。即使数据库中需要它们,您的代码也不必加载它们。
在您的查询表达式中,您可以确定 Entity Framework 不会在客户端执行它们,而是从中解析出一个 SQL 查询。
因此:
.Where(x => x.User!.Id == request.UserId)
你可以用User!
告诉编译器你知道它在那里不会为空。除非你启用了客户端评估,但你不应该这样做,如果你这样做了,无论如何你都需要一个空检查。
至于PostFile.User
的用法,如:
var postFile = dbContext.PostFiles.FirstOrDefault(p => p....) ?? throw ...;
var user = postFile.User;
如果您没有Include(p => p.User)
并且没有启用延迟加载,则它可以是null
,因此user
在使用前需要进行空检查。
如果您确实使用延迟加载,则可以禁用警告:
#pragma warning disable CS8618 // EF initializes these properties through lazy loading
public virtual User User get; set;
#pragma warning restore CS8618
【讨论】:
【参考方案3】:我认为你需要这个:
public class PostFile
public User User get; set;
public Post Post get; set;
然后调用
var postFiles = context.postFiles.Where(x => x.User.Id == request.UserId).Include(x => x.Post);
【讨论】:
没有。不将这些属性标记为可为空会使编译器发出警告,指出它们可能未初始化,这是真的。【参考方案4】:var postFiles = context.postFiles.Where(x => x.User != null && x.User.Id == request.UserId);
呢?
【讨论】:
以上是关于在 Entity Framework 6 查询中取消引用可能为空的引用的主要内容,如果未能解决你的问题,请参考以下文章
如何将带有 EntityState 和值的查询从 Entity Framework 5 转换为 6?
为啥 Entity Framework 6 会为简单的查找生成复杂的 SQL 查询?
Entity Framework Core 6.0 预览4 性能改进
《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集 (转)
《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句 (转)