【问题标题】:Dereference of a possibly null reference in Entity Framework 6 query在 Entity Framework 6 查询中取消引用可能为空的引用
【发布时间】: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

标签: c# entity-framework-core entity-framework-6 .net-6.0


【解决方案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 中的客户端评估。 :) 我预见到有关此警告或重大更改的许多 StackOverflow 问题,以及充斥着“!”的许多客户端代码库宽恕标记作为旧的不可空对象/引用被传递到代码中,并带有可空引用检查。

【讨论】:

    【解决方案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); 呢?

      【讨论】:

        猜你喜欢
        • 2020-05-07
        • 2015-04-13
        • 2021-12-22
        • 1970-01-01
        • 2021-04-17
        • 2015-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多