Как избежать вызова виртуальной функции в конструкторе?
В большинстве (если не все) мои сущности Framework POCOs имеют виртуальные функции. Мне нужно, чтобы эти функции были виртуальными, чтобы объекты могли быть ленивыми.
Если я инициализирую Accommodations
в конструкторе, тогда я буду вызывать виртуальную функцию в конструкторе, что является плохой практикой.
Но как я могу инициализировать Accommodations
, если не в конструкторе?
public class Venue
{
public Venue()
{
Accommodations = new HashSet<Accommodation>();
}
public virtual ICollection<Accommodation> Accommodations { get; set; }
}
Ответы
Ответ 1
public class Venue
{
private accommodations_ = new HashSet<Accommodation>();
public Venue() { }
public virtual ICollection<Accommodation> Accommodations
{
get { return accommodations_; }
set { accommodations_ = value; }
}
}
Ответ 2
Другой вариант - отметить установщик как закрытый. Это устраняет проблему вызова виртуальных членов в конструкторе.
Как только вы это сделаете, вам необходимо предоставить возможность для вызывающих абонентов (кроме EF) устанавливать это свойство по мере необходимости по вашему дизайну. Вы можете использовать перегруженный конструктор для передачи в списке мест или, возможно, инкапсулировать свою коллекцию (проект, управляемый доменом), и использовать методы для добавления/удаления элементов (обратите внимание: с EF это становится "хакерским", так как ему не хватает поддержки полностью инкапсулированных коллекции, в отличие от NHibernate):
public class Venue
{
public Venue()
{
Accommodations = new HashSet<Accommodation>();
}
public Venue(ICollection<Accommodation> accommodations)
{
Accommodations = new List<Accommodation>(accommodations);
}
public virtual ICollection<Accommodation> Accommodations { get; private set; }
}
Ответ 3
При ленивой загрузке вы даже не инициализируете Accommodations
до ее первого доступа, поэтому оставьте его null
.
Вы можете обертывать следующим образом, чтобы он автоматически инициализировал себя:
private ICollection<Accommodation> _accommodations;
public virtual ICollection<Accommodation> Accommodations {
get {
if (_accommodations == null)
{
// Initialize or load data here.
_accommodations = new HashSet<Accommodation>();
}
return _accomodations;
}
set {
_accommodations = value;
}
}
Обязательно прочтите комментарий ниже относительно этого решения!
Ответ 4
С инициализацией по умолчанию С# 6.0 его можно упростить без использования частных переменных:
public class Venue
{
public virtual ICollection<Accommodation> Accommodations {get; set; } = new HashSet<Accommodation>();
}