EF codefirst:我应该初始化导航属性吗?

我曾经看过一些书(例如编程entity framework代码第一个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; } } 

一些其他书籍或工具(例如Entity Framework Power Tools )在生成POCO时会初始化该类的导航属性,如:

 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; } } 

Q1:哪一个更好? 为什么? 优点和缺点?

编辑:

 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; } } 

Q2:在第二种方法中,如果`License`类也具有对'用户'类的引用,则会发生堆栈溢出。 这意味着我们应该有单向引用。(?)我们应该如何决定哪个导航属性应该被删除?

collections:没关系。

集合和引用在导航属性上有明显的区别。 参考一个实体。 一个集合包含实体。 这意味着初始化一个集合在业务逻辑方面是没有意义的 :它没有定义实体之间的关联。 设置一个参考。

所以纯粹是一个偏好问题,无论你是否或如何初始化embedded式列表。

就个人而言,我更喜欢懒惰的初始化:

 private ICollection<Address> _addresses; public virtual ICollection<Address> Addresses { get { return this._addresses ?? (this._addresses = new HashSet<Address>()); } 

它防止空引用exception,所以它有助于unit testing和操作集合,但也可以防止不必要的初始化。 后者可能会有所不同,当一个类有相对多的collections。 缺点是需要相对多的pipe道,尤其是pipe道。 当与没有初始化的自动属性相比时。

参考性质:不

引用属性是实体,因此为它们分配一个空对象是有意义的

更糟糕的是,如果您在构造函数中启动它们,EF在实现对象或延迟加载时不会覆盖它们。 他们将永远有他们的初始值,直到你积极取代他们。 更糟的是,你甚至可能最终将空实体保存在数据库中!

还有另一个影响: 关系fixup不会发生。 关系修正是EF通过导航属性连接上下文中的所有实体的过程。 当一个User和一个Licence分开加载时, User.License将被填充,反之亦然。 当然,除非在构造函数中初始化了License 。 1:n关联也是如此。 如果Address将在其构造函数中初始化User ,则User.Addresses将不会被填充!

在我所有的项目中, 我遵循这个规则 – “收集不应该是空的,它们要么是空的,要么是有价值的。

第一个例子是,当这些实体的创build是第三方代码(例如ORM)的责任并且您正在开发一个短时间项目时。

第二个例子比较好,因为

  • 您确定该实体具有所有属性设置
  • 你避免了愚蠢的NullReferenceException
  • 你让你的代码的消费者更快乐

实践领域驱动devise的人员将集合展示为只读,并避免使用setter。 (请参阅NHibernate中只读列表的最佳实践

Q1:哪一个更好? 为什么? 优点和缺点?

由于避免了代码中的额外检查(例如Addresses ),因此最好公开非空字符。 在您的代码库中有一个很好的合同。 但是,我可以公开可空的引用单个实体(如License

问题2:在第二种方法中,如果License类别也具有对User类别的引用,则会发生堆栈溢出。 这意味着我们应该有单向引用。(?)我们应该如何决定哪个导航属性应该被删除?

当我自己开发数据映射器模式时 ,我尝试避免双向引用,并且很less从子级到父级引用。

当我使用ORM时,很容易有双向引用。

当需要为使用双向引用集的unit testing构buildtesting实体时,请遵循以下步骤:

  1. 我与emty children collectionbuild立parent entity
  2. 然后,我把evey的childjoin到parent entityjoin到children collection

Licensetypes的无参数构造函数Insted我会使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; } } 

new列表是多余的,因为您的POCO依赖于延迟加载。

延迟加载是指一个实体或实体集合在第一次访问涉及实体/实体的属性时从数据库自动加载的过程。 在使用POCO实体types时,延迟加载是通过创build派生代理types的实例,然后覆盖虚拟属性来添加加载挂钩来实现的。

如果你想删除虚拟修饰符,那么你会closures延迟加载,在这种情况下,你的代码不再工作(因为没有什么会初始化列表)。

请注意,延迟加载是entity framework支持的function,如果您在DbContext的上下文之外创build该类,那么依赖代码显然将遭受NullReferenceException

HTH

Q1:哪一个更好? 为什么? 优点和缺点?

在实体构造函数中设置虚拟属性的第二个变体有一个确定的问题,称为“ 构造函数中的虚拟成员调用 ”。

至于没有导航属性初始化的第一个变体,取决于谁创build了一个对象有两种情况:

  1. entity framework创build一个对象
  2. 代码使用者创build一个对象

当entity framework创build一个对象时,第一个变体是完全有效的,但是当代码使用者创build一个对象时可能会失败。

确保代码使用者始终创build有效对象的解决scheme是使用静态工厂方法 :

  1. 使默认构造函数受到保护。 entity framework适用于受保护的构造函数。

  2. 添加一个创build一个空对象的静态工厂方法,例如一个User对象,在创build之后设置所有属性,例如AddressesLicense ,并返回一个完全构造的User对象

这种方式entity framework使用受保护的默认构造函数从数据源获得的数据创build一个有效的对象和代码使用者使用静态工厂方法来创build一个有效的对象。

我用这个答案为什么是我的entity framework代码第一个代理收集为空,为什么我不能设置它?

有构造函数初始化的问题。 我这样做的唯一原因是使testing代码更容易。 确保收集永远不会空,使我不断地在testing等初始化