Nice programing

EF codefirst : 탐색 속성을 초기화해야합니까?

nicepro 2020. 12. 12. 12:27
반응형

EF codefirst : 탐색 속성을 초기화해야합니까?


일부 책 (예 : 프로그래밍 엔티티 프레임 워크 코드 먼저 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; }
}

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

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` 클래스에`User` 클래스에 대한 참조도 있으면 스택 오버플로가 발생합니다. 단방향 참조가 있어야 함을 의미합니다. (?) 탐색 속성 중 제거 할 속성을 어떻게 결정해야합니까?


컬렉션 : 상관 없습니다.

탐색 속성으로서 컬렉션과 참조 간에는 뚜렷한 차이가 있습니다. 참조 엔티티입니다. 컬렉션 에는 엔터티 가 포함 됩니다. 즉, 컬렉션을 초기화하는 것은 비즈니스 논리 측면에서 의미없으며 엔터티 간의 연결을 정의하지 않습니다. 참조를 설정하면됩니다.

따라서 포함 된 목록을 초기화할지 여부 또는 방법은 순전히 선호도의 문제입니다.

"방법"에 관해서는 어떤 사람들은 지연 초기화를 선호합니다.

private ICollection<Address> _addresses;

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

null 참조 예외를 방지하므로 단위 테스트 및 컬렉션 조작이 용이하지만 불필요한 초기화도 방지됩니다. 후자는 클래스에 비교적 많은 컬렉션이있을 때 차이를 만들 수 있습니다. 단점은 상대적으로 많은 배관이 필요하다는 것입니다. 초기화하지 않고 자동 속성과 비교할 때. 또한 C #에서 null 전파 연산자의 출현으로 컬렉션 속성을 초기화하는 것이 시급하지 않게되었습니다.

... 명시 적 로딩이 적용되지 않는 한

유일한 것은 컬렉션을 초기화하면 컬렉션이 Entity Framework에서로드되었는지 여부를 확인하기 어렵다는 것입니다. 컬렉션이 초기화되면 다음과 같은 문이 표시됩니다.

var users = context.Users.ToList();

... User비어 있고 null이 아닌 Addresses컬렉션을 가진 객체를 생성 합니다 (게으른로드 옆에 있음). 컬렉션이로드되었는지 확인하려면 다음과 같은 코드가 필요합니다.

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

컬렉션이 초기화되지 않은 경우 간단한 null검사가 수행됩니다. 따라서 선택적 명시 적 로딩이 코딩 관행의 중요한 부분 인 경우, 즉 ...

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

... 컬렉션 속성을 초기화하지 않는 것이 더 편리 할 수 ​​있습니다.

참조 속성 : 안 함

참조 속성은 엔터티이므로 빈 개체를 할당하는 것이 의미가 있습니다.

더 나쁜 것은 생성자에서 시작하면 EF가 개체를 구체화하거나 지연로드 할 때 덮어 쓰지 않습니다. 적극적으로 교체 할 때까지 항상 초기 값을 갖게 됩니다. 더 나쁜 것은 데이터베이스에 빈 엔티티를 저장하게 될 수도 있다는 것입니다!

그리고 또 다른 효과가 있습니다 : 관계 수정 이 일어나지 않을 것입니다. 관계 수정은 EF가 컨텍스트의 모든 엔터티를 탐색 속성으로 연결하는 프로세스입니다. a User와 a Licence가 별도로로드되면 여전히 User.License채워지며 그 반대의 경우도 마찬가지입니다. 물론 License생성자에서 초기화 되지 않는 한 . 이것은 1 : n 연결에서도 마찬가지입니다. 경우 Address를 초기화 할 User생성자에서, 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. 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. So, in conclusion, also in EF-core initializing reference navigation properties in constructors may cause trouble. 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 (e.g. ORM) and you are working on a short-time project.

Second example is better, since

  • you are sure that entity has all properties set
  • you avoid silly NullReferenceException
  • you make consumers of your code happier

People, who practice Domain-Driven Design, expose collections as read-only and avoid setters on them. (see What is the best practice for readonly lists in NHibernate)

Q1: Which one is better? why? Pros and Cons?

It is better to expose not-null colections since you avoid additional checks in your code (e.g. Addresses). It is a good contract to have in your codebase. But it os OK for me to expose nullable reference to single entity (e.g. License)

Q2: In second approach there would be stack overflow if the License class has a reference to User class too. 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.

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.
  2. Then I add evey child with reference to parent entity into children collection.

Insted of having parameterless constructor in License type I would make user property required.

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.

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.

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).

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

HTH


Q1: Which one is better? 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.

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, e.g. a User object, sets all properties, e.g. Addresses and License, after creation and returns a fully constructed User object

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:

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. Therefore, I'd initialize all collections as HashSets.

And personally, I think it'd be pretty slick to tweak the above to take advantage of C# 6.0's auto-property initializers:

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

참고URL : https://stackoverflow.com/questions/20757594/ef-codefirst-should-i-initialize-navigation-properties

반응형