c# - 在 Entity Framework Core 中编写实体 POCO 类的正确方法是什么?

标签 c# .net entity-framework asp.net-core entity-framework-core

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

令人惊讶的是,官方 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< T > 类型,而不仅仅是 List< T >。
  • 导航属性在构造函数中被初始化为new HashSet< T >()。在代码优先示例中没有这样的初始化。
  • 导航属性被声明为虚拟
  • 生成的上下文类中的 DbSet 成员也是虚拟的

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

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

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

请尽快帮助我理解为什么会有所不同,以及在代码中构建模型时应该使用哪种样式?

最佳答案

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

  • partial 类对于工具生成的代码特别有用。假设您要实现一个仅限模型的派生属性。对于代码优先,您可以随心所欲地进行。对于数据库优先,如果更新模型,类文件将被重写。所以如果你想保留你的扩展代码,你想把它放在托管模型之外的不同文件中 - 这是 partial 帮助你扩展类的地方,而无需手动调整自动生成的代码.

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

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

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

  • 虚拟 IDbSet 上下文条目允许更轻松地设计用于单元测试的测试模型上下文。其他用例也可能存在。

关于c# - 在 Entity Framework Core 中编写实体 POCO 类的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38954108/

相关文章:

c# - 在不知道 T 的情况下保留 List<T> 的引用?

c# - 使用 while 或 for? 查找 array 中的最大数字?

c# - DynamoDB .NET - 从表中删除所有项目

entity-framework - 具有多个父属性、单个子集合的 EF6 自引用表

时间:2019-03-17 标签:c#EntityFrameworkGroupBywithjoin

c# - 在 EF 中作为 IQueryable 的表值函数?

c# - FTP ://prompts for app 的 Process.Start

c# - System.Threading.Timer 和 49.7 天计时器滚动更新

.net - Horizo​​ntalAlignment 设置为 Stretch 和 Left 的 TextBox

.net - 如何在使用 Office 2003 打开 Office 2007 文档时禁用转换消息?