0

В моих UserRepository У меня есть GetActive метод:Отложенная казни в хранилище шаблона

public IEnumerable<User> GetActive() 
{ 
    var users = context.Set<UserTbl>().Where(x => x.IsActive); 

    foreach(var user in users) 
    { 
     yield return entityMapper.CreateFrom(user); 
    } 
} 

entityMapper используются для отображения от EF-порожденной UserTbl к доменным User сущности.

Существует тысячи пользователей, поэтому я хочу, чтобы метод GetActive отложил выполнение, возвращая IEnumerable<User>, так что весь список не извлекается без необходимости. Я сделал это выше с foreach и yield.

При тестировании, кажется, что все данные извлекаются независимо. Следующие два вызова принимают одно и то же время:

// Get only 5 users in memory 
var someUsers = UserRepository.GetActive().Take(5).ToList(); 

// Get all 100,000 users into memory 
var allUsers = UserRepository.GetActive().ToList(); 

Что я делаю неправильно?

+1

Самый простой способ - включить разбиение на страницы как аргументы метода репозитория «GetActive (int skip, int take)» – MikeSW

ответ

1

В тот момент, когда вы используете foreach, данные перечислены. Вы должны использовать IQueryable только до ToList. Ваша идея об отсрочке данных с использованием IEnumerable выглядит неплохо, но это неправильно. IEnumerable всегда возвращает все данные, это просто не заставляет провайдера данных хранить все это в памяти. Вы должны использовать IQueryable, если вы хотите, чтобы поставщик возвращал части данных. Но тогда вы не можете использовать foreach и yield, потому что он всегда перечисляет все данные в своем параметре. Единственный способ сделать то, что вы хотите, - передать требуемый запрос в метод GetActive.

public IEnumerable<User> GetActive(Func<IQueryable<User>, IQueryable<User>> modifier) 
{ 
    var users = modifier(context.Set<UserTbl>().Where(x => x.IsActive)); 

    foreach(var user in users) 
    { 
     yield return entityMapper.CreateFrom(user); 
    } 
} 

// Get only 5 users in memory 
var someUsers = UserRepository.GetActive(q=>q.Take(5)).ToList(); 

// Get all 100,000 users into memory 
var allUsers = UserRepository.GetActive(q=>q).ToList(); 

Но я бы порекомендовал не иметь репозиториев в вашей архитектуре вообще. Они вводят излишнюю сложность по сравнению с уже сложным ORM. См. Больше в Repository pattern with Entity framework

+1

Я бы рекомендовал ** использовать ** репозитории, если вы хотите, чтобы приложение было отделено от сохранения , ORM или нет, это не имеет значения. Если вы используете ORM, не делайте ошибку, чтобы определить объект домена поверх объекта ORM. Репозиторий может с радостью использовать ORM, не подвергая его воздействию, выступая в роли высокоуровневого картографа и, самое главное, как слой антикоррозийной защиты. – MikeSW

+0

Спасибо за ваш ответ на мой вопрос, Euphoric. Хотя, если что-либо, использование репозиториев отвлекло многие сложности данных, с которыми мы имеем дело, и обеспечило высокую степень проверки на нашей кодовой базе. – davenewza

Смежные вопросы