Должны ли мы инициировать коллекции в классах Entity? [Дубликат]

Проблема, с которой вы сталкиваетесь, связана с тем, как числа с плавающей запятой представлены на компьютере. Более подробное обсуждение представлений с плавающей запятой появляется к концу моего ответа (раздел «Плавающее представление»). Версия TL; DR: поскольку компьютеры имеют ограниченное количество памяти, цифры могут быть представлены только с конечной точностью. Таким образом, точность чисел с плавающей запятой ограничена определенным количеством десятичных знаков (около 16 значащих цифр для значений с двойной точностью , по умолчанию используется в MATLAB).

Фактическая и отображаемая точность

Теперь, чтобы обратиться к конкретному примеру в вопросе ... в то время как 24.0000 и 24.0000 отображаются в виде таким же образом, оказывается, что в этом случае они фактически отличаются очень маленькими десятичными суммами. Вы не видите этого, потому что MATLAB показывает только 4 значащие цифры по умолчанию , сохраняя общий дисплей аккуратным и аккуратным. Если вы хотите увидеть полную точность, вы должны либо выдать команду format long, либо просмотреть шестнадцатеричное представление числа:

>> pi
ans =
    3.1416
>> format long
>> pi
ans =
   3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18

Инициализированные значения по сравнению с вычисленными значениями

Поскольку существует только конечное число значений, которые могут быть представлены для числа с плавающей запятой, возможно, чтобы вычисление привело к значению, которое попадает между двумя из этих представлений. В таком случае результат должен быть округлен до одного из них. Это приводит к небольшой ошибке машинной точности . Это также означает, что инициализация значения напрямую или некоторым вычислением может дать несколько иные результаты. Например, значение 0.1 не имеет точного представления с плавающей запятой (т. Е. Оно слегка округляется), и поэтому вы получаете контр-интуитивные результаты, подобные этому из-за способа ошибки округления:

>> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]);  % Sum 10 0.1s
>> b=1;                                               % Initialize to 1
>> a == b
ans =
  logical
   0                % They are unequal!
>> num2hex(a)       % Let's check their hex representation to confirm
ans =
3fefffffffffffff
>> num2hex(b)
ans =
3ff0000000000000

Как правильно обрабатывать сравнения с плавающей запятой

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

a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end

отобразит «Равно!».

Затем вы можете изменить свой код на что-то вроде:

points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
                (abs(points(:,2)-vertex1(2)) > tolerance),:)

Представление с плавающей запятой

Хороший обзор чисел с плавающей запятой (и, в частности, стандарта IEEE 754 для арифметики с плавающей запятой ) Что должен знать каждый компьютерный ученый Арифметика с плавающей запятой Дэвида Голдберга

Бинарное число с плавающей запятой фактически представлено тремя целыми числами: знаковым битом s, значением (или коэффициентом / фракцией) b , и экспонентой e. Для формата с плавающей запятой с двойной точностью каждое число представлено 64 битами, выложенными в памяти следующим образом:

Реальное значение может быть найдено по следующей формуле:

Этот формат допускает числовые представления в диапазоне от 10 ^ -308 до 10 ^ 308. Для MATLAB вы можете получить эти ограничения из realmin и realmax :

>> realmin
ans =
    2.225073858507201e-308
>> realmax
ans =
    1.797693134862316e+308

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

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

>> eps(1)
ans =
     2.220446049250313e-16
>> eps(1000)
ans =
     1.136868377216160e-13

Обратите внимание, что точность относительный к размеру представленного числа; большие числа будут иметь большие расстояния между представлениями с плавающей запятой и, следовательно, будут иметь меньше цифр точности после десятичной точки. Это может быть важным соображением при некоторых расчетах. Рассмотрим следующий пример:

>> format long              % Display full precision
>> x = rand(1, 10);         % Get 10 random values between 0 and 1
>> a = mean(x)              % Take the mean
a =
   0.587307428244141
>> b = mean(x+10000)-10000  % Take the mean at a different scale, then shift back
b =
   0.587307428244458

Обратите внимание, что когда мы смещаем значения x из диапазона [0 1] в диапазон [10000 10001], вычисляем среднее значение, а затем вычитаем среднее смещение для сравнения , мы получаем значение, которое отличается для последних 3 значащих цифр. Это иллюстрирует, как смещение или масштабирование данных могут изменять точность выполненных на нем вычислений, что должно быть связано с определенными проблемами.

52
задан Iman Mahmoudinasab 25 April 2016 в 05:04
поделиться

6 ответов

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

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

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

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

private ICollection<Address> _addresses;

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

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

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

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

var users = context.Users.ToList();

... создаст объекты User, имеющие пустые, а не нулевые 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 не будет перезаписывать их при материализации вашего объекта или ленивой загрузкой. Они всегда будут иметь свои начальные значения, пока вы активно не замените их. Хуже того, вы даже можете сохранить пустые объекты в базе данных!

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

Ядро Entity Framework

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

61
ответ дан Gert Arnold 18 August 2018 в 12:14
поделиться
  • 1
    вау, никогда не видел такого, кажется хорошей идеей, но никогда не видел ни в одном проекте / книге. +1 для специального ответа. Но я боюсь использовать это. Я обновляю свой вопрос, не могли бы вы сделать тот же метод для не-коллекционных ссылочных свойств? – Iman Mahmoudinasab 1 January 2014 в 21:25
  • 2
    Нет, потому что бесполезно устанавливать ссылочные свойства с объектами по умолчанию. Эти ссылки имеют деловое значение, поэтому они должны быть либо null, либо ссылаться на осмысленные объекты, которые установлены специально. – Gert Arnold 1 January 2014 в 23:08
  • 3
    Отключить тему и в конце дня, но почему вы, например, используете HashSet? – stovroz 18 April 2016 в 14:46
  • 4
    @stovroz Это один из нескольких вариантов. HashSet оптимизирован для быстрого поиска и гарантированно содержит уникальные объекты. EF генерирует HashSets по умолчанию с помощью t4 или code-first из базы данных. – Gert Arnold 18 April 2016 в 14:51
  • 5
    любое объяснение относительно того, почему set не включено? – user919426 11 July 2017 в 10:12
  • 6
    – Artificial Stupidity 31 January 2018 в 21:42

Это избыточно для списка new, так как ваш POCO зависит от Lazy Loading.

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

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

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

НТН

3
ответ дан bas 18 August 2018 в 12:14
поделиться
  • 1
    Я быстро научился отключать ленивую загрузку во многих сценариях, потому что это заставляет вас эффективно получать ваши данные. Итак, да: я всегда инициализирую навигационные коллекции (и сложные типы), но никогда не обладаю исключительными свойствами ссылки. – Dabblernl 24 December 2013 в 13:40

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

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

4
ответ дан Community 18 August 2018 в 12:14
поделиться
  • 1
    Я обновил свой вопрос, не могли бы вы проверить второй вопрос и добавить дополнительные сведения к своему ответу. – Iman Mahmoudinasab 1 January 2014 в 21:15
  • 2
    Я добавил ответ на второй вопрос. Основная идея, что коллекции не должны быть обнуляемыми, но можно избежать ссылки с дочернего на родителя. – Ilya Palkin 2 January 2014 в 00:49
  • 3
    Спасибо за Ваш ответ. Не могли бы вы объяснить, что вы подразумеваете под First example is possible to have when creation of these entities is responsibility of third-part code и test-entity? И не могли бы вы добавить свой адрес электронной почты в свой профиль. Спасибо снова. – Iman Mahmoudinasab 2 January 2014 в 17:35
  • 4
    test-entity является фиктивным или заглушкой, который вы используете в своих модульных тестах. В этом случае third-part code является ORM. Итак, First example is possible, когда вы используете ORM, который инициализирует ваши сущности / коллекции, когда у вас есть общая инфраструктура, которая гарантирует, что коллекции не равны нулю. – Ilya Palkin 2 January 2014 в 19:12

Другие ответы полностью отвечают на вопрос, но я хотел бы добавить что-то, потому что этот вопрос по-прежнему имеет значение и появляется в поиске 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>();
0
ответ дан Jesse Hufstetler 18 August 2018 в 12:14
поделиться
4
ответ дан Community 6 September 2018 в 23:46
поделиться
4
ответ дан Community 30 October 2018 в 04:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: