Ima*_*sab 59 c# domain-driven-design entity-framework navigation-properties ef-code-first
我曾经看过一些书(例如编程实体框架代码Julia Lerman)定义了他们的域类(POCO)而没有初始化导航属性,如:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Address { get; set; }
public virtual License License { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
生成POCO时,其他一些书籍或工具(例如Entity Framework Power Tools)初始化类的导航属性,如:
public class User
{
public User()
{
this.Addresses = new IList<Address>();
this.License = new License();
}
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual License License { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
编辑:
public class License
{
public License()
{
this.User = new User();
}
public int Id { get; set; }
public string Key { get; set; }
public DateTime Expirtion { get; set; }
public virtual User User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Ger*_*old 72
集合和引用之间存在明显差异,即导航属性.引用是一个实体.集合包含实体.这意味着在业务逻辑方面初始化集合是没有意义的:它没有定义实体之间的关联.设置参考确实.
因此,无论是否或如何初始化嵌入式列表,都只是一个偏好问题.
至于"如何",有些人更喜欢延迟初始化:
private ICollection<Address> _addresses;
public virtual ICollection<Address> Addresses
{
get { return this._addresses ?? (this._addresses = new HashSet<Address>());
}
Run Code Online (Sandbox Code Playgroud)
它可以防止空引用异常,因此它有助于单元测试和操作集合,但它也可以防止不必要的初始化.当一个类有相对多的集合时,后者可能会有所不同.缺点是它需要相对多的管道,尤其是.与没有初始化的自动属性相比.此外,C#中的零传播运算符的出现使得初始化集合属性变得不那么紧迫.
...除非应用显式加载
唯一的问题是初始化集合使得很难检查Entity Framework是否加载了集合.如果一个集合被初始化,那么声明如...
var users = context.Users.ToList();
Run Code Online (Sandbox Code Playgroud)
...将创建User
具有空的非空集合的对象Addresses
(延迟加载).检查是否加载了集合需要像......这样的代码
var user = users.First();
var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded;
Run Code Online (Sandbox Code Playgroud)
如果未初始化集合,则null
可以执行简单检查.因此,当选择性显式加载是编码实践的重要部分时,即......
if (/*check collection isn't loaded*/)
context.Entry(user).Collection(c => c.Addresses).Load();
Run Code Online (Sandbox Code Playgroud)
...初始化集合属性可能更方便.
引用属性是实体,因此为它们分配空对象是有意义的.
更糟糕的是,如果您在构造函数中启动它们,EF在实现对象或延迟加载时不会覆盖它们.在您主动替换它们之前,它们将始终具有初始值.更糟糕的是,您甚至可能最终在数据库中保存空实体!
还有另一个影响:关系修复不会发生.关系修正是EF通过其导航属性连接上下文中的所有实体的过程.当a User
和a Licence
分别加载时,仍将User.License
填充,反之亦然.当然,除非License
在构造函数中初始化.对于1:n关联也是如此.如果在其构造函数中Address
初始化a User
,则User.Addresses
不会填充!
实体框架核心
Entity Framework核心中的关系修正(在编写本文时为2.1)不受构造函数中初始化的引用导航属性的影响.也就是说,当单独从数据库中提取用户和地址时,将填充导航属性.
然而,延迟加载并没有覆盖初始化参考导航性能.因此,总而言之,在EF-core中初始化构造函数中的引用导航属性可能会带来麻烦.不要这样做.无论如何,这没有意义,
在我所有的项目中,我都遵循规则——“集合不应为空。它们要么是空的,要么有值。”
当创建这些实体是第三方代码(例如 ORM)的责任并且您正在处理一个短期项目时,第一个示例是可能的。
第二个例子更好,因为
NullReferenceException
实践领域驱动设计的人们将集合公开为只读,并避免使用 setter。(请参阅NHibernate 中只读列表的最佳实践是什么)
Q1:哪个更好?为什么?利弊?
最好公开非空集合,因为您可以避免在代码中进行额外检查(例如Addresses
)。在您的代码库中拥有一份很好的合同。但是我可以公开对单个实体的可空引用(例如License
)
Q2:在第二种方法中,如果License
类也有对User
类的引用,则会出现堆栈溢出。这意味着我们应该有单向引用。(?)我们应该如何决定应该删除哪一个导航属性?
当我自己开发数据映射器模式时,我尽量避免双向引用,并且很少有从子级到父级的引用。
当我使用 ORM 时,很容易有双向引用。
当需要使用双向参考集为我的单元测试构建测试实体时,我遵循以下步骤:
parent entity
用 emty构建children collection
。child
参考parent entity
into children collection
。如果在License
类型中使用无参数构造函数,我将需要user
属性。
public class License
{
public License(User user)
{
this.User = user;
}
public int Id { get; set; }
public string Key { get; set; }
public DateTime Expirtion { get; set; }
public virtual User User { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
22158 次 |
最近记录: |