如何将 C# 8.0 可空引用类型与 Entity Framework Core 模型一起使用?

Posted

技术标签:

【中文标题】如何将 C# 8.0 可空引用类型与 Entity Framework Core 模型一起使用?【英文标题】:How to use C# 8.0 Nullable Reference Types with Entity Framework Core models? 【发布时间】:2020-01-29 23:41:52 【问题描述】:

我在 .NET Core 3.0 项目上启用 C# 8.0 Nullable Reference Types。项目使用Entity Framework Core 3.0访问数据库。

下面是一个Title不能为null的数据模型。

public class Vehicle

    public int Id  get; private set;  

    public string Title  get; private set; 

    // Entity Framework Core is instructed to bind to the private _drivers field in a configuration builder
    private readonly List<Driver> _drivers = new List<Driver>();
    public IReadOnlyCollection<Driver> Drivers => _drivers.AsReadOnly();

    private Vehicle() 
    
    

    public Vehicle(string title) 
    
        this.Title = title;
    

    public void AddDriver(string name)
    
         this._drivers.Add(new Driver(name));
    
 

// A foreign column is defined in a configuration builder
public class Driver

    public int Id  get; private set;  

    public string Name  get; private set; 

    private Driver() 
    
    

    public Driver(string name) 
    
        this.Name = name;
    
 

自己的代码应该只使用 public 构造函数,而 private 构造函数只是为了允许 Entity Framework Core 和(可能还有)序列化将值从数据库绑定到这些类/模型。公共构造函数可能具有与模型具有的属性不同的结构、列表和参数类型(例如,它可能还包含第一个必需子项的参数,它可能具有一些可选参数等)。

但是,编译器会在 private 构造函数上生成 CS8618 Non-nullable field is uninitialized. Consider declaring as nullable.

我可以通过 #pragma warning disable CS8618private 构造函数禁用 CS8616,但我认为这不是一个好主意。

在这种情况下应该如何使用 C# 8.0 可空引用类型? 还是我的模型是伪造的或违反了最佳实践 - 如何正确执行?

很遗憾,我没有找到相关的文档或指南。

【问题讨论】:

docs.microsoft.com/en-us/ef/core/miscellaneous/… ? 【参考方案1】:

没有适当的方法来处理不可为空的导航属性。

    文档提出了两种方法,但都不是类型安全的。用一个 支持字段并抛出 InvalidOperationException。不清楚如何 它不同于什么都不做并且有一个 NullReferenceException 使用 null 宽恕运算符抑制它

官方文档链接:https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#non-nullable-properties-and-initialization

【讨论】:

【参考方案2】:

我同意你的看法——pragma 块很难看。相反,我会将 null 宽恕运算符分配给默认构造函数 inside 的不可为 null 的引用类型,如下所示:

private Vehicle() 

    Title = null!;

这比使用上述操作符内联初始化属性更简洁,更具表现力,如下所示:

public string Title  get; private set;  = null!;

后一种解决方案读作“我知道Title 在任何情况下都不为空”,这实际上否定了不可为空引用类型的好处,因为您丢失了所有设计时检查。前者读作“我知道Title此特定 场景中不为空”,因此如果您错过分配,编译器警告会继续在其他地方发出。

【讨论】:

您可能希望使用 default 关键字而不是 null。 public string Title get; private set; = default!;。这样,您可以对值类型遵循相同的模式。【参考方案3】:

来自MS Docs for Entity types with constructors

当 EF Core 创建这些类型的实例时,例如为结果 的查询,它会首先调用默认的无参数构造函数 然后将每个属性设置为数据库中的值。然而,如果 EF Core 找到一个带有参数名称的参数化构造函数,并且 匹配映射属性的类型,然后它将改为调用 具有这些属性的值的参数化构造函数和 不会显式设置每个属性。

也许值得创建一个带有这些属性所需参数的私有 ctor,然后看看框架是否会调用它并工作?

除非您完全 100% 确信可以禁用警告,否则禁用警告也不是一个好主意。

【讨论】:

以上是关于如何将 C# 8.0 可空引用类型与 Entity Framework Core 模型一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

有没有一种方便的方法来过滤一系列 C# 8.0 可空引用,只保留非空值?

如何将所有 C# 8 可空引用警告视为错误?

使用 C# 8 的可空引用类型

C# 8中的可空引用类型

可空引用类型 - 通过接受的参数返回类型可空性

可空引用类型和选项模式