Ленивая загрузка - какой лучший подход?
Я видел многочисленные примеры ленивой загрузки - какой ваш выбор?
Для класса модели, например:
public class Person
{
private IList<Child> _children;
public IList<Child> Children
{
get {
if (_children == null)
LoadChildren();
return _children;
}
}
}
Класс Person не должен знать ничего о том, как его загружают дети.... или не так ли? Разумеется, он должен контролировать, когда свойства заполнены, или нет?
У вас есть репозиторий, который связывает Person вместе со своей дочерней коллекцией или вы бы использовали другой подход, например, используя класс lazyload - даже тогда, я не хотите, чтобы класс lazyload размывался в моей модели.
Как бы вы справлялись с производительностью, если сначала запрашивали личность, а затем его детей (т.е. не ленивую загрузку в этом случае) или как-то ленивую загрузку.
Все это сводится к личным выборам?
Ответы
Ответ 1
Лучшая ленивая загрузка позволяет избежать этого;) Безопасность потоков - это немедленная проблема, с которой вам придется обращаться. Я не помню, как часто я видел, что производственные системы с 8 ядрами процессора работают с ленивой загрузкой 8 раз для каждой используемой ленивой модели загрузки. По крайней мере, при запуске сервера все серверные ядра имеют тенденцию попадать в одни и те же места.
Пусть каркас DI построит его для вас, если вы можете. И если вы не можете, я по-прежнему предпочитаю явное построение. Таким образом, все виды магии AOP просто не режут меня, идите для явного построения вне класса. Не помещайте его в класс человека, просто создайте службу, которая правильно построит объекты.
Представляя "магические" слои, которые более или менее прозрачно делают эти вещи, кажется приятной идеей, но мне еще не встречаются реализации, которые не имеют непредвиденных и проблемных последствий.
Ответ 2
Вы можете использовать класс Lazy<T>
, о котором я говорил здесь:
Каков правильный способ ввода зависимости доступа к данным для ленивой загрузки?
Существует также ссылка на более подробное сообщение в блоге...
Ответ 3
Я говорил о решении, которое я использую для выполнения ленивой загрузки здесь
Ответ 4
Вы можете использовать шаблон Virtual Proxy вместе с Схема наблюдателя. Это даст вам ленивую загрузку без класса Person, имеющего явные знания о том, как загружаются дети.
Ответ 5
Я думаю, что это именно та проблема, которая лучше всего обрабатывается АОП (например, PostSharp). Сделайте свою ленивую загрузку в качестве аспекта, а затем используйте ее, чтобы украсить любое свойство, которое вы хотите загрузить лениво. Отказ от ответственности: не пробовал; просто думая, что он должен работать.
Ответ 6
Я просто задал связанный с ним вопрос здесь, но он был более тяжелым в отношении неизменяемости и безопасности потоков. Много хороших ответов и комментариев. Вы можете найти это полезным.
Ответ 7
Вот пример реализации ленивой загрузки с использованием прокси-шаблона
Класс Person, который будет жить с остальными вашими моделями. Дети отмечены как виртуальные, поэтому их можно переопределить внутри класса PersonProxy.
public class Person {
public int Id;
public virtual IList<Child> Children { get; set; }
}
Класс PersonRepository, который будет жить с остальными вашими репозиториями. Я включил метод, чтобы получить детей в этом классе, но вы могли бы иметь его в классе ChildRepository, если хотите.
public class PersonRepository {
public Person FindById(int id) {
// Notice we are creating PersonProxy and not Person
Person person = new PersonProxy();
// Set person properties based on data from the database
return person;
}
public IList<Child> GetChildrenForPerson(int personId) {
// Return your list of children from the database
}
}
Класс PersonProxy, который живет с вашими репозиториями. Это наследуется от Лица и будет выполнять ленивую загрузку. Вы также можете использовать логическое значение, чтобы проверить, было ли оно уже загружено, вместо проверки, если дети == null.
public class PersonProxy : Person {
private PersonRepository _personRepository = new PersonRepository();
public override IList<Child> Children {
get {
if (base.Children == null)
base.Children = _personRepository.GetChildrenForPerson(this.Id);
return base.Children;
}
set { base.Children = value; }
}
}
Вы можете использовать его так:
Person person = new PersonRepository().FindById(1);
Console.WriteLine(person.Children.Count);
Конечно, вы могли бы заставить PersonProxy использовать интерфейс PersonRepository и получить доступ ко всему этому через службу, если вы не хотите напрямую обращаться к PersonRepository.