默认情况下,EF Core具有“代码优先心态”,即它应该以代码优先的方式使用,即使支持数据库优先方法,它也只能进行逆向工程。现有数据库并创建它的代码优先表示。我的意思是,在代码“手工”(代码优先)中创建并从数据库(通过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类。它在文档中随处可见(除了从数据库生成的示例)。第二个例子有一些补充:
我尝试过从数据库中构建模型(截至本文撰写时的最新工具)并且它完全按照所示生成实体,因此这不是一个过时的文档问题。因此官方工具生成不同的代码,官方文档建议编写不同的(普通的)代码 - 没有部分类,虚拟成员,构造初始化等。
我的问题是,尝试在代码中构建模型,我应该如何编写代码?我喜欢使用ICollection而不是List,因为它更通用,但除此之外,我不确定我是否需要遵循文档或MS工具?我需要将它们声明为虚拟吗?我是否需要在构造函数中初始化它们?等...
我从旧的EF时代知道虚拟导航属性允许延迟加载,但在EF Core中甚至不支持它(我),我不知道任何其他用途。也许它会影响性能?也许工具试图生成面向未来的代码,这样当实现延迟加载时,POCO类和上下文将能够支持它吗?如果是这样,我可以抛弃它们,因为我不需要延迟加载(所有数据查询都封装在一个回购中)?
很快,请帮助我理解为什么不同,在代码中构建模型时应该使用哪种样式?
答案 0 :(得分:13)
我试着简单回答你提到的每一点
partial
类对工具生成的代码特别有用。假设您要实现仅模型派生属性。对于代码优先,您只需要在任何地方执行此操作。对于数据库优先,如果更新模型,将重写类文件。因此,如果您想保留扩展代码,则需要将其放在托管模型之外的其他文件中 - 这是partial
帮助您扩展类而无需手动调整自动生成代码的地方。
ICollection
绝对是一个合适的选择,即使是代码优先。如果没有排序声明,您的数据库可能不会支持已定义的订单。
构造函数初始化至少是一种方便...假设您有一个数据库方式的空集合,或者根本没有加载该属性。如果没有构造函数,则必须在代码中的任意点处明确处理null
个案例。您是否应该使用List
或HashSet
是我现在无法回答的问题。
virtual
可以为数据库实体创建代理,这可以帮助您完成两件事:如您所述的延迟加载和更改跟踪。代理对象可以使用setter立即跟踪虚拟属性的更改,而上下文中的普通对象需要在SaveChanges
上进行检查。在某些情况下,这可能更有效(通常不是)。
virtual IDbSet
上下文条目可以更轻松地设计单元测试的测试模型上下文。其他用例也可能存在。