关系导航

EF Core 关系由外键定义。 导航在外键上分层,以提供用于读取和操作关系的自然、面向对象的视图。 通过使用导航,应用程序可以处理实体图,而无需关注外键值出现的情况。

导航有两种形式:引用和集合。 引用导航是对另一个实体的简单对象引用。 它们表示一对多和一对一关系中的“一”方。

导航有两种形式:引用和集合。 引用导航是对另一个实体的简单对象引用。 它们表示一对多和一对一关系中的“一”方。 例如:

C#


public Blog TheBlog { get; set; }
引用导航必须具有资源库,尽管它不需要是公共资源库。 引用导航不应自动初始化为非空默认值,这样做等于断言不存在的实体存在。

使用 C# 可为空引用类型时,对于可选关系,引用导航必须为 空:

C#


public Blog? TheBlog { get; set; }
必需关系的引用导航可以为空,也可以不为空。

 

集合导航
集合导航是 .NET 集合类型的实例,即任何实现 ICollection<T> 的类型。 集合包含相关实体类型的实例,其中可以有任何数字。 它们表示引用一对多和多对多关系中的“多”方。 例如:

C#


public ICollection<Post> ThePosts { get; set; }
集合导航不需要具有资源库。 内联初始化集合很常见,因此,如果属性为 null,则无需检查。 例如:

C#


public ICollection<Post> ThePosts { get; } = new List<Post>();

 

数组不能用于集合导航,因为即使它们实现了 ICollection<T>,Add 方法在调用时也会引发异常。

即使集合实例必须是 ICollection<T>,也不需要以这种方式公开集合。 例如,通常会将导航公开为 IEnumerable<T>,用于提供无法由应用程序代码随机修改的只读视图。 例如:

C#

复制
public class Blog
{
public int Id { get; set; }
public IEnumerable<Post> ThePosts { get; } = new List<Post>();
}
此模式的变体包括根据需要操作集合的方法。 例如:

C#

复制
public class Blog
{
private readonly List<Post> _posts = new();

public int Id { get; set; }

public IEnumerable<Post> Posts => _posts;

public void AddPost(Post post) => _posts.Add(post);
}
应用程序代码仍然可以将公开的集合强制转换为 ICollection<T>,然后对其进行操作。 如果这是一个问题,则实体可以返回集合的防御性副本。 例如:

C#

复制
public class Blog
{
private readonly List<Post> _posts = new();

public int Id { get; set; }

public IEnumerable<Post> Posts => _posts.ToList();

public void AddPost(Post post) => _posts.Add(post);
}
仔细考虑从中获取的值是否足够高,以至超过每次访问导航时创建集合副本的开销。

提示

此最终模式之所以有效,是因为默认情况下,EF 通过其支持字段访问集合。 这意味着 EF 本身从实际集合中添加和移除实体,而应用程序仅与集合的防御性副本交互。

集合导航的初始化
集合导航可由实体类型预先初始化:

C#

复制
public class Blog
{
public ICollection<Post> Posts { get; } = new List<Post>();
}
或事后初始化:

C#

复制
public class Blog
{
private ICollection<Post>? _posts;

public ICollection<Post> Posts => _posts ??= new List<Post>();
}
例如,如果 EF 需要将实体添加到集合导航,则在执行查询时,它将初始化集合(如果当前为 null)。 创建的实例取决于导航的公开类型。

如果导航公开为 HashSet<T>,则会创建使用 ReferenceEqualityComparer 的 HashSet<T> 实例。
否则,如果导航公开为具有无参数构造函数的具体类型,则会创建该具体类型的实例。 这适用于 List<T>,但也适用于其他集合类型,包括自定义集合类型。
否则,如果导航公开为 IEnumerable<T>、ICollection<T> 或 ISet<T>,则会创建使用 ReferenceEqualityComparer 的 HashSet<T> 实例。
否则,如果导航公开为 IList<T>,则会创建 List<T> 实例。
否则会引发异常。

posted @ 2024-03-26 13:23  一个人走在路上  阅读(9)  评论(0编辑  收藏  举报