EF codefirst: должен ли я инициализировать свойства навигации?



Я видел некоторые книги(например Программирование entity framework 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; }
}


некоторые другие книги или инструменты (например Entity Framework Power Tools) когда генерирует POCOs инициализирует свойства навигации класса, например:



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: какой из них лучше? зачем? Плюсы и Минусы?



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: во втором подходе будет переполнение стека, если класс "лицензия" также имеет ссылку на класс "пользователь". Это значит, что у нас должна быть односторонняя ссылка.(?) Как мы должны решить, какое из свойств навигации должно быть удалено?

711   6  

6 ответов:

коллекции: это не имеет значения.

существует четкое различие между коллекциями и ссылками в качестве свойств навигации. Ссылка и сущности. А коллекции содержит объекты. Это означает, что инициализация коллекции бессмысленно С точки зрения бизнес-логики: он не определяет ассоциацию между сущностями. Установка ссылки делает.

так что это чисто вопрос предпочтения ли или нет, или как вы инициализируете встроенные списки.

Что касается "как", некоторые люди предпочитают ленивую инициализацию:

private ICollection<Address> _addresses;

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

он предотвращает исключения нулевой ссылки, поэтому он облегчает модульное тестирование и манипулирование коллекцией, но также предотвращает ненужную инициализацию. Последнее может иметь значение, когда класс имеет относительно много коллекций. Недостатком является то, что он занимает относительно много сантехники, особенно. по сравнению с автоматическими свойствами без инициализации. Кроме того, появление оператора null-propagation в C# сделало менее срочной инициализацию свойств коллекции.

...если не применяется явная загрузка

единственное, что инициализация коллекций затрудняет проверку того, была ли коллекция загружена Entity Framework. Если коллекция инициализирована, оператор like...

var users = context.Users.ToList();

...создадим User объекты, имеющие пустой, не-null Addresses сборники (ленивая загрузка в сторону). Проверка того, загружена ли коллекция, требует такого кода...

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

если коллекция не инициализируется простой проверка. Поэтому, когда выборочная явная загрузка является важной частью вашей практики кодирования, т. е...

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

...возможно, удобнее не инициализировать свойства коллекции.

свойства ссылки: не

Справочник по свойствам являются юридическими лицами, поэтому, назначая пустой объектом для них является значимую.

хуже того, если вы инициируете их в конструкторе, EF не будет перезаписывать их при материализации объекта или при ленивой загрузке. Они всегда будут иметь свои начальные значения, пока вы активно заменить их. Хуже того, вы можете даже в конечном итоге сохранить пустые объекты в базе данных!

и есть еще один эффект:исправление отношения не occcur. Исправление отношений - это процесс, с помощью которого EF соединяет все объекты в контексте по их свойствам навигации. Когда User и Licence загружаются отдельно, еще User.License будет заселен и наоборот. Если, конечно, если License был инициализирован в конструкторе. Это также верно для ассоциаций 1:n. Если Address инициализирует a User в своем конструкторе, User.Addresses не будет заселен!

Entity Framework core

исправление отношений в ядре Entity Framework (2.1 на момент написания статьи) не зависит от инициализированных ссылочных навигационных свойств в конструкторах. То есть, когда пользователи и адреса извлекаются из базы данных отдельно, свойства навигации заполняются.
Однако ленивая загрузка делает не перезаписать инициализированные свойства навигации по ссылкам. Таким образом, в заключение, также в EF-core инициализация ссылочных навигационных свойств в конструкторах может вызвать проблемы. Не делай этого. Это не имеет смысла во всяком случае,

во всех моих проектах я следую правилу - " коллекции не должны быть null. Они либо пусты, либо имеют значения."

первый пример можно иметь, когда создание этих объектов является ответственностью кода третьей части (например, ORM), и вы работаете над краткосрочным проектом.

второй пример лучше, так как

  • вы уверены, что сущность имеет все свойства set
  • вы избегаете глупо NullReferenceException
  • вы делаете потребителей вашего кода счастливее

люди, которые практикуют доменный дизайн, предоставляют коллекции только для чтения и избегают сеттеров на них. (см. что является лучшей практикой для списков только для чтения в NHibernate)

Q1: какой из них лучше? зачем? Плюсы и минусы?

лучше выставлять NOT-null colections, так как вы избегаете дополнительных проверок в своем коде (например Addresses). Это хороший контракт, чтобы иметь в вашей кодовой базе. Но это нормально для меня, чтобы выставить нулевую ссылку на один объект (например,License)

Q2: во втором подходе было бы переполнение стека, если License класс имеет ссылку на User класс тоже. Это значит, что у нас должна быть односторонняя ссылка.(?) Как мы должны решить, какое из свойств навигации должно быть удалено?

когда я разработал Data mapper pattern by сам я старался избегать двунаправленных ссылок и очень редко имел ссылку от ребенка к родителю.

когда я использую ORMs, легко иметь двунаправленные ссылки.

когда необходимо построить test-entity для моих модульных тестов с двунаправленным набором ссылок, я выполняю следующие шаги:

  1. я строю parent entity С виду children collection.
  2. затем я добавляю Иви child со ссылкой на parent entity в children collection.

Insted наличия конструктора без параметров в 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; }
}

это избыточно для new список, так как ваш POCO зависит от ленивой загрузки.

отложенная загрузка-это процесс, при котором сущность или коллекция сущностей автоматически загружается из базы данных при первом обращении к свойству, ссылающемуся на сущность/сущности. При использовании типов сущностей POCO отложенная загрузка достигается путем создания экземпляров производных прокси-типов, а затем переопределения виртуальных свойств для добавления крючка загрузки.

Если вы удалите виртуальный модификатор, то вы отключите ленивую загрузку, и в этом случае ваш код больше не будет работать (потому что ничто не будет инициализировать список).

обратите внимание, что ленивая загрузка-это функция, поддерживаемая Entity framework, если вы создаете класс вне контекста DbContext, то зависящий код, очевидно, будет страдать от NullReferenceException

HTH

Q1: какой из них лучше? зачем? Плюсы и минусы?

второй вариант, когда виртуальные свойства задаются внутри конструктора сущности, имеет определенную проблему, которая называется"вызов виртуального члена в конструкторе".

Что касается первого варианта без инициализации свойств навигации, есть 2 ситуации в зависимости от того, кто / что создает объект:

  1. Entity framework создает объект
  2. потребитель кода создает объект

первый вариант вполне допустим, когда Entity Framework создает объект, но может произойти сбой, когда потребитель кода создает объект.

решение для обеспечения того, что потребитель кода всегда создает допустимый объект, заключается в использовании статический метод фабрики:

  1. сделать конструктор по умолчанию защищены. Entity Framework отлично подходит для работы с защищенными проектировщики.

  2. добавьте статический заводской метод, который создает пустой объект, например a User объект, задает все свойства, например Addresses и License, после создания и возвращает полностью построенный User объект

таким образом, Entity Framework использует защищенный конструктор по умолчанию для создания допустимого объекта из данных, полученных из некоторого источника данных, а потребитель кода использует статический заводской метод для создания допустимого объекта.

Я использую ответ из этого почему мой Entity Framework Code First proxy collection null и почему я не могу его установить?

возникли проблемы с инициализацией конструктора. Единственная причина, по которой я делаю это, чтобы сделать тестовый код проще. Убедившись, что коллекция никогда не равна нулю, я постоянно инициализируюсь в тестах и т. д.

другие ответы полностью отвечают на вопрос, но я хотел бы добавить что-то, так как этот вопрос по-прежнему актуален и появляется в поиске google.

при использовании мастера "код первой модели из базы данных" в 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; }
}

Я склонен считать вывод мастера в основном официальной рекомендацией от Microsoft,поэтому я добавляю к этому пятилетнему вопросу. Таким образом, Я бы инициализировал все коллекции как HashSet s.

и лично я думаю, что было бы довольно гладко настроить выше, чтобы воспользоваться преимуществами инициализаторов автоматического свойства C# 6.0:

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

Comments

    Ничего не найдено.