在实体框架核心中,写实体POCO类的正确方法是什么?

本文关键字:实体 是什么 方法 POCO 核心 框架 | 更新日期: 2023-09-27 18:02:37

EF Core在默认情况下具有"代码优先的心态",即它应该以代码优先的方式使用,即使支持数据库优先的方法,它也被描述为对现有数据库进行逆向工程并创建代码优先的表示。我的意思是,在代码中"手工"(代码优先)创建的模型(POCO类)和从数据库(通过Scaffold-DbContext命令)生成的模型应该是相同的。

令人惊讶的是,官方EF Core文档显示了显著的差异。下面是用代码创建模型的示例:https://ef.readthedocs.io/en/latest/platforms/aspnetcore/new-db.html下面是从现有数据库进行逆向工程的示例:https://ef.readthedocs.io/en/latest/platforms/aspnetcore/existing-db.html

这是第一种情况下的实体类:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public List<Post> Posts { get; set; }
}
public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

,这是实体类在第二种情况下:

public partial class Blog
{
    public Blog()
    {
        Post = new HashSet<Post>();
    }
    public int BlogId { get; set; }
    public string Url { get; set; }
    public virtual ICollection<Post> Post { get; set; }
}

第一个例子是一个非常简单、非常明显的POCO类。它在文档中随处可见(除了从数据库生成的示例)。第二个例子有一些补充:

  • 类被声明为partial(即使没有地方看到它的另一个partial定义)。
  • 导航属性的类型为ICollection,而不仅仅是List
  • 导航属性初始化为new HashSet<构造函数中的T>()。在代码优先的例子中没有这样的初始化。
  • 导航属性被声明为虚拟
  • 生成的上下文类中的DbSet成员也是虚的

我已经尝试了从数据库搭建模型(撰写本文时的最新工具),它生成的实体完全如图所示,所以这不是过时的文档问题。因此,官方工具生成不同的代码,官方文档建议编写不同的(琐碎的)代码——没有部分类、虚拟成员、构造初始化等。

我的问题是,试图在代码中建立模型,我应该如何写我的代码?我喜欢使用ICollection而不是List,因为它更通用,但除此之外,我不确定我是否需要遵循文档,还是MS工具?我需要将它们声明为虚拟的吗?我需要在构造函数中初始化它们吗?等等…

我知道从旧的EF时代,虚拟导航属性允许延迟加载,但它甚至不支持(尚未)在EF核心,我不知道任何其他用途。也许它会影响性能?也许工具会尝试生成面向未来的代码,以便在实现延迟加载时,POCO类和上下文能够支持它?如果是这样,我可以放弃它们,因为我不需要延迟加载(所有的数据查询都封装在一个repo中)?

简单地说,请帮助我理解为什么会有差异,以及我在代码中构建模型时应该使用哪种风格?

在实体框架核心中,写实体POCO类的正确方法是什么?

我尽量对你提到的每一点都作一个简短的回答

  • partial类对于工具生成的代码特别有用。假设您想要实现一个仅由模型派生的属性。首先是代码,你可以在任何你想要的地方做。首先,如果您更新您的模型,类文件将被重写。所以如果你想保留你的扩展代码,你想把它放在托管模型之外的一个不同的文件中——这就是partial帮助你扩展类的地方,而不需要手工调整自动生成的代码。

  • ICollection绝对是一个合适的选择,即使是代码优先。如果没有排序语句,您的数据库可能不支持已定义的顺序。

  • 构造函数初始化至少是一种方便…假设在数据库方面有一个空集合,或者根本没有加载属性。如果没有构造函数,您必须在代码中的任意点显式地处理null情况。你应该用List还是HashSet,我现在还不能回答。

  • virtual为数据库实体启用代理创建,这可以帮助实现两件事:延迟加载(您已经提到过)和更改跟踪。代理对象可以使用setter立即跟踪虚拟属性的变化,而上下文中的普通对象需要在SaveChanges上进行检查。在某些情况下,这可能更有效(不是一般)。

  • virtual IDbSet上下文条目允许更容易地设计单元测试的测试模型上下文。其他用例也可能存在。