简体   繁体   English

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

[英]EF codefirst : Should I initialize navigation properties?

I had seen some books(eg programming entity framework code first Julia Lerman ) define their domain classes (POCO) with no initialization of the navigation properties like:我看过一些书(例如编程实体框架代码首先 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; }
}

some other books or tools (eg Entity Framework Power Tools ) when generates POCOs initializes the navigation properties of the the class, like:其他一些书籍或工具(例如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: Which one is better? Q1:哪个更好? why?为什么? Pros and Cons?利弊?

Edit:编辑:

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: In second approach there would be stack overflow if the `License` class has a reference to `User` class too. Q2:在第二种方法中,如果 `License` 类也引用了 `User` 类,则会出现堆栈溢出。 It means we should have one-way reference.(?) How we should decide which one of the navigation properties should be removed?这意味着我们应该有单向引用。(?)我们应该如何决定应该删除哪一个导航属性?

Collections: It doesn't matter.收藏:没关系。

There is a distinct difference between collections and references as navigation properties.作为导航属性的集合和引用之间存在明显区别。 A reference is an entity.引用一个实体。 A collections contains entities.集合包含实体。 This means that initializing a collection is meaningless in terms of business logic: it does not define an association between entities.这意味着初始化集合在业务逻辑方面毫无意义:它没有定义实体之间的关联。 Setting a reference does.设置参考确实如此。

So it's purely a matter of preference whether or not, or how, you initialize embedded lists.因此,是否或如何初始化嵌入列表纯粹是一个偏好问题。

As for the "how", some people prefer lazy initialization:至于“如何”,有些人更喜欢延迟初始化:

private ICollection<Address> _addresses;

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

It prevents null reference exceptions, so it facilitates unit testing and manipulating the collection, but it also prevents unnecessary initialization.它可以防止空引用异常,因此它有助于单元测试和操作集合,但它也可以防止不必要的初始化。 The latter may make a difference when a class has relatively many collections.当一个类有相对较多的集合时,后者可能会有所不同。 The downside is that it takes relatively much plumbing, esp.缺点是它需要相对较多的管道,尤其是。 when compared to auto properties without initialization.与没有初始化的自动属性相比。 Also, the advent of the null-propagation operator in C# has made it less urgent to initialize collection properties.此外,C# 中空传播运算符的出现使得初始化集合属性变得不那么紧迫。

...unless explicit loading is applied ...除非应用显式加载

The only thing is that initializing collections makes it hard to check whether or not a collection was loaded by Entity Framework.唯一的问题是初始化集合使得很难检查实体框架是否加载了集合。 If a collection is initialized, a statement like...如果一个集合被初始化,像这样的语句...

var users = context.Users.ToList();

...will create User objects having empty, not-null Addresses collections (lazy loading aside). ...将创建具有空的、非空的Addresses集合的User对象(延迟加载放在一边)。 Checking whether the collection is loaded requires code like...检查集合是否已加载需要像...

var user = users.First();
var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded;

If the collection is not initialized a simple null check will do.如果集合未初始化,则将执行简单的null检查。 So when selective explicit loading is an important part of your coding practice, ie ... So when selective explicit loading is an important part of your coding practice, ie ...

if (/*check collection isn't loaded*/)
    context.Entry(user).Collection(c => c.Addresses).Load();

...it may be more convenient not to initialize collection properties. ...不初始化集合属性可能更方便。

Reference properties: Don't参考属性:不要

Reference properties are entities, so assigning an empty object to them is meaningful .引用属性是实体,因此为它们分配一个空对象是有意义的

Worse, if you initiate them in the constructor, EF won't overwrite them when materializing your object or by lazy loading.更糟糕的是,如果您在构造函数中启动它们,EF 不会在具体化您的对象或通过延迟加载时覆盖它们。 They will always have their initial values until you actively replace them.在您主动替换它们之前,它们将始终具有初始值。 Worse still, you may even end up saving empty entities in the database!更糟糕的是,您甚至可能最终将空实体保存在数据库中!

And there's another effect: relationship fixup won't occcur.还有另一个效果:不会发生关系修复。 Relationship fixup is the process by which EF connects all entities in the context by their navigation properties.关系修复是 EF 通过其导航属性连接上下文中所有实体的过程。 When a User and a Licence are loaded separately, still User.License will be populated and vice versa.UserLicence分别加载时,仍会填充User.License ,反之亦然。 Unless of course, if License was initialized in the constructor.当然,除非在构造函数中初始化了License This is also true for 1:n associations.对于 1:n 关联也是如此。 If Address would initialize a User in its constructor, User.Addresses would not be populated!如果Address会在其构造函数中初始化UserUser.Addresses不会填充User.Addresses

Entity Framework core实体框架核心

Relationship fixup in Entity Framework core (2.1 at the time of writing) isn't affected by initialized reference navigation properties in constructors.实体框架核心(撰写本文时为 2.1)中的关系修复不受构造函数中初始化的引用导航属性的影响。 That is, when users and addresses are pulled from the database separately, the navigation properties are populated.也就是说,当分别从数据库中提取用户和地址时,会填充导航属性。
However, lazy loading does not overwrite initialized reference navigation properties.然而,延迟加载不会覆盖初始化参考导航性能。

In EF-core 3, initializing a reference navigation property prevents Include from working properly.在 EF-core 3 中,初始化参考导航属性会阻止Include正常工作。

So, in conclusion, also in EF-core, initializing reference navigation properties in constructors may cause trouble.因此,总而言之,同样在 EF-core 中,在构造函数中初始化引用导航属性可能会导致麻烦。 Don't do it.不要这样做。 It doesn't make sense anyway.反正也没意义。

In all my projects I follow the rule - "Collections should not be null. They are either empty or have values."在我所有的项目中,我都遵循规则——“集合不应为空。它们要么是空的,要么有值。”

First example is possible to have when creation of these entities is responsibility of third-part code (eg ORM) and you are working on a short-time project.当创建这些实体是第三方代码(例如 ORM)的责任并且您正在处理一个短期项目时,第一个示例是可能的。

Second example is better, since第二个例子更好,因为

  • you are sure that entity has all properties set您确定该实体已设置所有属性
  • you avoid silly NullReferenceException你避免愚蠢的NullReferenceException
  • you make consumers of your code happier你让你的代码的消费者更快乐

People, who practice Domain-Driven Design, expose collections as read-only and avoid setters on them.实践领域驱动设计的人们将集合公开为只读,并避免使用 setter。 (see What is the best practice for readonly lists in NHibernate ) (请参阅NHibernate 中只读列表的最佳实践是什么

Q1: Which one is better? Q1:哪个更好? why?为什么? Pros and Cons?利弊?

It is better to expose not-null colections since you avoid additional checks in your code (eg Addresses ).最好公开非空集合,因为您可以避免在代码中进行额外检查(例如Addresses )。 It is a good contract to have in your codebase.在您的代码库中拥有一份很好的合同。 But it os OK for me to expose nullable reference to single entity (eg License )但是我可以公开对单个实体的可空引用(例如License

Q2: In second approach there would be stack overflow if the License class has a reference to User class too. Q2:在第二种方法中,如果License类也有对User类的引用,则会出现堆栈溢出。 It means we should have one-way reference.(?) How we should decide which one of the navigation properties should be removed?这意味着我们应该有单向引用。(?)我们应该如何决定应该删除哪一个导航属性?

When I developed data mapper pattern by myself I tryed to avoid bidirectional references and had reference from child to parent very rarely.当我自己开发数据映射器模式时,我尽量避免双向引用,并且很少有从子级到父级的引用。

When I use ORMs it is easy to have bidirectional references.当我使用 ORM 时,很容易有双向引用。

When it is needed to build test-entity for my unit-tests with bidirectional reference set I follow the following steps:当需要使用双向参考集为我的单元测试构建测试实体时,我遵循以下步骤:

  1. I build parent entity with emty children collection .我使用 emty children collection构建parent entity
  2. Then I add evey child with reference to parent entity into children collection .然后我将参考parent entity每个child添加到children collection

Insted of having parameterless constructor in License type I would make user property required.如果在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; }
}

It's redundant to new the list, since your POCO is depending on Lazy Loading. new列表是多余的,因为您的 POCO 依赖于延迟加载。

Lazy loading is the process whereby an entity or collection of entities is automatically loaded from the database the first time that a property referring to the entity/entities is accessed.延迟加载是在第一次访问引用实体或实体的属性时自动从数据库加载实体或实体集合的过程。 When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook.使用 POCO 实体类型时,通过创建派生代理类型的实例,然后覆盖虚拟属性以添加加载钩子来实现延迟加载。

If you would remove the virtual modifier, then you would turn off lazy loading, and in that case your code no longer would work (because nothing would initialize the list).如果您要删除 virtual 修饰符,那么您将关闭延迟加载,在这种情况下,您的代码将不再起作用(因为没有任何东西可以初始化列表)。

Note that Lazy Loading is a feature supported by entity framework, if you create the class outside the context of a DbContext, then the depending code would obviously suffer from a NullReferenceException请注意,延迟加载是实体框架支持的功能,如果您在 DbContext 的上下文之外创建类,那么依赖代码显然会遭受NullReferenceException

HTH HTH

Q1: Which one is better? Q1:哪个更好? why?为什么? Pros and Cons?利弊?

The second variant when virtual properties are set inside an entity constructor has a definite problem which is called " Virtual member call in a constructor ".在实体构造函数中设置虚拟属性时的第二个变体有一个明确的问题,称为“ 构造函数中的虚拟成员调用”。

As for the first variant with no initialization of navigation properties, there are 2 situations depending on who / what creates an object:至于第一个没有初始化导航属性的变体,有两种情况取决于谁/什么创建了一个对象:

  1. Entity framework creates an object实体框架创建对象
  2. Code consumer creates an object代码消费者创建一个对象

The first variant is perfectly valid when Entity Framework creates a object, but can fail when a code consumer creates an object.第一个变体在 Entity Framework 创建对象时完全有效,但在代码使用者创建对象时可能会失败。

The solution to ensure a code consumer always creates a valid object is to use a static factory method :确保代码使用者始终创建有效对象的解决方案是使用静态工厂方法

  1. Make default constructor protected.保护默认构造函数。 Entity Framework is fine to work with protected constructors.实体框架适用于受保护的构造函数。

  2. Add a static factory method that creates an empty object, eg a User object, sets all properties, eg Addresses and License , after creation and returns a fully constructed User object添加创建空对象(例如User对象)的静态工厂方法,在创建后设置所有属性(例如AddressesLicense并返回完全构造的User对象

This way Entity Framework uses a protected default constructor to create a valid object from data obtained from some data source and code consumer uses a static factory method to create a valid object.这样,实体框架使用受保护的默认构造函数从从某些数据源获得的数据创建有效对象,而代码使用者使用静态工厂方法创建有效对象。

I use the answer from this Why is my Entity Framework Code First proxy collection null and why can't I set it?我使用这个答案为什么我的实体框架代码优先代理集合为空,为什么我不能设置它?

Had problems with constructor initilization.构造函数初始化有问题。 Only reason I do this is to make test code easier.我这样做的唯一原因是使测试代码更容易。 Making sure collection is never null saves me constantly initialising in tests etc确保集合永远不会为空可以节省我在测试等中不断初始化

The other answers fully answer the question, but I'd like to add something since this question is still relevant and comes up in google searches.其他答案完全回答了这个问题,但我想补充一些东西,因为这个问题仍然相关并且会出现在谷歌搜索中。

When you use the "code first model from database" wizard in Visual Studio all collections are initialized like so:当您在 Visual Studio 中使用“数据库中的代码第一个模型”向导时,所有集合都像这样初始化:

public partial class SomeEntity
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public SomeEntity()
    {
        OtherEntities = new HashSet<OtherEntity>();
    }

    public int Id { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<OtherEntity> OtherEntities { get; set; }
}

I tend to take wizard output as basically being an official recommendation from Microsoft, hence why I'm adding to this five-year-old question.我倾向于认为向导输出基本上是 Microsoft 的官方推荐,因此我为什么要添加到这个 5 年前的问题。 Therefore, I'd initialize all collections as HashSet s.因此,我将所有集合初始化为HashSet

And personally, I think it'd be pretty slick to tweak the above to take advantage of C# 6.0's auto-property initializers:就我个人而言,我认为调整上述内容以利用 C# 6.0 的自动属性初始值设定项会非常巧妙:

    public virtual ICollection<OtherEntity> OtherEntities { get; set; } = new HashSet<OtherEntity>();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 EF CodeFirst - 添加ICollection导航属性 - EF CodeFirst - Adding ICollection Navigation Properties EF 核心 3.1:在使用急切加载加载相关实体时,我应该初始化列表导航属性吗? - EF core 3.1: should I initialize list navigation properties when using eager loading to load related entities? EF 4.3和CodeFirst:一对多导航属性在没有实体代理的情况下加载为空值 - EF 4.3 & CodeFirst: One-to-many navigation properties loads as nulls without entity proxy 忽略EF CodeFirst中所有已实现的接口属性 - Ignoring all implemented interface properties in EF CodeFirst 我应该为每个域在DDD中创建一个单独的数据库吗?EF MVC codefirst - Should i have for each Domain a separate database in DDD?EF MVC codefirst 具有 EF 导航属性的 Automapper - Automapper with EF Navigation Properties EF导航属性不起作用? - EF Navigation Properties not working? 具有导航属性的EF SqlQuery - EF SqlQuery with navigation properties 如何在EF 6.1 CodeFirst中的视图上添加导航属性 - How can you add a navigation property on a view in EF 6.1 CodeFirst 模型中的EF访问导航属性 - EF access navigation properties in model
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM