导航属性是否可以为空

问题描述 投票:0回答:2

看看这个实体:

class EntityA
{
   public int Id { get;set; }
   public string Name { get;set; }
   public int? ClientId { get; set; }

   // Navigation properties:
   public ClientEntity? Client { get; set; }
}

如您所见,该实体包含一个可选属性:ClientId。这意味着客户端是可选的。在这种情况下,sql server 数据库中的 ClientId 字段将包含 NULL。

我正在使用外键的导航属性:这是“客户端”属性。当 ClientId 为 null 时,Client 也应该为 null。

这就是为什么我声明:“ClientEntity?”键入 Client 属性。

但我看到有人在相同的情况下声明“ClientEntity”(不可为空)。 但我不明白他们如何在这种情况下操纵空客户端......

有什么想法吗?

谢谢

entity-framework linq
2个回答
0
投票

将这个留给在为项目启用可空性时也遇到此问题的任何人,导航属性不应该为空。

根据微软,

集合导航包含对多个相关实体的引用,应始终不可为空。空集合意味着不存在相关实体,但列表本身不应该为空。

https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types

所以编码的解决方案是:

class EntityA
{
   public int Id { get;set; }
   public string Name { get;set; }
   public int ClientId { get; set; }

   // Navigation properties:
   public ClientEntity Client { get; set; } = null!;
}

0
投票

这个很好的问题缺少一个很好的答案,所以对于任何来到这里寻求指导的人......

实体定义

让我们想象一个具有一些基本属性

Customer
FullName
的实体
Age
FullName
必须提供,
Age
是可选的。

每个客户**必须有一个地址并且可以有一个折扣代码,这两个重要实体存储在同一个数据库中,但存储在不同的表中

此外,

Customer
实体在数据库中对于
Order
s具有1:N关系。

所以...我们基本上拥有所有可能遇到的选择。

public class Customer
{
   // In DB, set as mandatory, but EF take care about this.
   // By default, initiated to 0
   public int Id {get; set;}
   
   // In DB, set as mandatory, we could take care about this,
   // or set it up in DB as auto value.
   // By default initiated on Guid.Empty.
   public Guid UniqueId {get; set;}
   
   // In DB, set as mandatory.
   public string FullName {get; set;}
   
   // In Db, set as optional (can have null).
   public int Age {get; set;}
   
   // In Db, set as mandatory.
   public int AddressId {get; set;}
   
   public Address Address {get; set;}
      
   // In Db, set as optional relationship (it might not exists).
   public int DiscountCodeId {get; set;}
   
   public DiscountCode DiscountCode {get; set;}

   public ICollection<Order> Orders {get; set;}
}

可能的方法

首先,您必须区分可以由某些基本类型(

int
double
datetime
string
等)表示的基本值和表示导航的任何非基本对象财产。

其次,你必须设定你想要遵循的标准,要么是防御性的,要么是乐观的。

以下是 .NET 8 和 C# 12 两种案例的展示。

防守方法

public class Customer
{
   // In DB, set as mandatory.
   public int Id {get; set;}
   
   // In DB, set as mandatory.
   public Guid UniqueId {get; set;}
   
   // In DB, set as mandatory.
   public required string FullName {get; set;}
   
   // In Db, set as optional (can have null).
   public int? Age {get; set;}
   
   // In Db, set as mandatory.
   public required int AddressId {get; set;}
   
   public Address? Address {get; set;}
      
   // In Db, set as optional relationship.
   public int? DiscountCodeId {get; set;}
   
   public DiscountCode? DiscountCode {get; set;}

   public ICollection<Order> Orders {get; set;} = [];
}

FullName
被标记为
required
,因为它是 DB 所要求的。

AddressId
可以标记为必填,但这取决于情况。每次您想要创建
Customer
实例时都需要它,这在编写测试时可能会很乏味。一般来说,我会根据需要进行设置,并使用构建器模式在测试中创建
Customer
。”

Address
设置为可为空,因为必须加载
Address
才能成为
null
。在防御方法中,我们希望在使用时强制进行
null
检查,因为它不需要加载。

Age
设置为可为空,因为它不是必需的,并且可能是
null

DiscountCodeId
设置为可为空,因为它不是必需的,并且可能是
null

DiscountCode
被设置为可为空,因为整个关系可为空,因此当
DiscountCodeId
null
时,
DiscountCode
必须相应地为
null

Orders
未标记为
null
,因为如果给定客户没有订单,则应仅通过空集合来指示。因此,我们只需使用空列表来初始化该属性。如果有任何订单,EF Core 将使用订单集合覆盖此设置。

此设置将生成不可为空引用类型警告。

乐观的态度

public class Customer
{
   // In DB, set as mandatory.
   public int Id {get; set;}
   
   // In DB, set as mandatory.
   public Guid UniqueId {get; set;}
   
   // In DB, set as mandatory.
   public required string FullName {get; set;}
   
   // In Db, set as optional (can have null).
   public int? Age {get; set;}
   
   // In Db, set as mandatory.
   public int AddressId {get; set;}
   
   public Address Address {get; set;} = null!
      
   // In Db, set as optional relationship.
   public int? DiscountCodeId {get; set;}
   
   public DiscountCode? DiscountCode {get; set;}

   public ICollection<Order> Orders {get; set;} = [];
}

唯一的区别在于导航属性。所有其他情况保持不变。在这种方法中,我们期望自动加载

Address

AddressId
未按要求标记,开发者负责在创建该类的新对象时正确设置该值。

Address
设置为不可空,这会在实例初始化后触发有关可能为空值的编译器警告。这是对的。该警告被空宽恕运算符抑制
= null!

© www.soinside.com 2019 - 2024. All rights reserved.