6

В EF 6 я хотел бы асинхронно обрабатывать объекты по мере их возврата из механизма базы данных.Асинхронно обрабатывать объекты, поскольку они возвращаются из механизма базы данных.

Я знаю, что могу позвонить ToListAsync() и ForEachAsync() и, возможно, они делают то, что ищу, но я не уверен. Я думаю, что я ищу, это сочетание двух.

С моей точки зрения, ToListAsync() выполнит задачу, когда весь запрос будет прочитан из механизма базы данных и преобразован в объекты. Это означает, что вам нужно дождаться возврата всего запроса до начала процесса.

Я не могу определить, выполняет ли ForEachAsync() то, что я ищу, но то, что я предполагаю, из-за отсутствия информации в другом месте, заключается в том, что ForEachAsync() просто работает с уже полученной коллекцией, и обработка каждого элемента асинхронна ,

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

Итак, действительно ли это ForEachAsync(), а если нет, есть ли способ для этого?

Причины желания этого являются два аспекта:

  1. Большие запросы не должны быть сохранены в памяти, как и весь коллекции, так что экономия объема памяти и способности обрабатывать гораздо больше результирующих не ограничен памятью
  2. продолжительность всего процесса , вероятно, будет короче, за счет обработки каждого элемента во время латентности поиска данных

Обновление: В принципе, если DbContext поднял событие, например, OnEntityLoaded для каждого объекта, когда вы позвонили LoadAsync(), я мог бы выполнить все, что захочу. Поскольку я мог бы просто вставить объект в отдельный процессор задач, и сущности могли бы эффективно обрабатываться и использовать любую задержку ввода-вывода. Я всегда могу настроить отдельный процессор задач, поэтому мне не нужен EF для поддержки асинхронной обработки сущностей, просто асинхронно загружать и запускать событие/вызывать делегат при загрузке каждого объекта.

Обновление 2: И если ForEachAsync() было вызвано, как сущности загружались, то это также выполнило бы то, что я хочу.

+0

Итак, вы ищете что-то, что автоматически извлекает элементы из базы данных в кусках или по одному за раз? Как правило, запрос EF вытаскивает несколько элементов из базы данных в одном запросе БД. – JLRishe

+0

Да, это именно то, что я ищу. В результате я хотел бы, чтобы асинхронный считыватель данных (например, SqlDataReader) асинхронно испускал/обрабатывал объекты. Я мог бы написать что-то с async SqlDataReader, но я хотел бы, чтобы он использовал объекты EF6 и работал в EF6. – MikeJansen

+0

@JLRishe, я добавил редактирование вопроса, чтобы уточнить. – MikeJansen

ответ

2

ForEachAsync, в отличие от ToListAsync, не получает все предметы заранее и просто позволяет вам перебирать его. Итерация составляет async.

QueryableExtensions.ForEachAsync делегатов к IDbAsyncEnumerable.ForEachAsyncwhich is this:

internal static async Task ForEachAsync(
    this IDbAsyncEnumerable source, Action<object> action, CancellationToken cancellationToken) 
{ 
    DebugCheck.NotNull(source); 
    DebugCheck.NotNull(action); 

    cancellationToken.ThrowIfCancellationRequested(); 

    using (var enumerator = source.GetAsyncEnumerator()) 
    { 
     if (await enumerator.MoveNextAsync(cancellationToken).WithCurrentCulture()) 
     { 
      Task<bool> moveNextTask; 
      do 
      { 
       cancellationToken.ThrowIfCancellationRequested(); 
       var current = enumerator.Current; 
       moveNextTask = enumerator.MoveNextAsync(cancellationToken); 
       action(current); 
      } 
      while (await moveNextTask.WithCurrentCulture()); 
     } 
    } 
} 

Вы можете видеть, что это очень похоже на то, как производится итерация над IEnumerable но async-await в виду.Вместо IEnumerable, GetEnumerator, IEnumerator и MoveNext у нас есть IDbAsyncEnumerable, GetAsyncEnumerator, IDbAsyncEnumerator и MoveNextAsync.

MoveNextAsync позволяет фактически асинхронно извлекать элементы по мере необходимости.

+0

Итак, 'ForEachAsync' зависит от реализации' IDbAsyncEnumerable' для правильной реализации, а не просто для обеспечения псевдо-асинхронного интерфейса. Мне нужно будет либо выполнить профилирование при отладке большого набора данных, либо выкопать код провайдера SQL Server EF. Угадать профилирование даст мне более быстрый ответ :) – MikeJansen

+0

@MikeJansen 'QueryableExtensions.ForEachAsync' работает на' IQueryable', но не может предположить, что 'IQueryable' имеет' MoveNextAsync', поэтому он проверяет, является ли параметр в этом случае 'IDbAsyncEnumerable' и в противном случае выдает исключение. Если вы используете 'ForEachAsync' и не получаете исключения, вы используете' IDbAsyncEnumerableExtensions.ForEachAsync' – i3arnon

+0

RIght, то, что я получаю, заключается в том, что реализация IDbAsyncEnumerable должна перечислить по мере поступления данных, а не только перечислите «асинхронно» после того, как данные будут локальными. Я не вижу ничего в контракте или описании, которое говорит о том, как «IDbAsyncEnumerable» необходимо перечислить в связи с извлечением данных, и нереально, что асинхронные шаблоны будут реализованы менее чем оптимально. Ваш ответ был очень полезен, и я отметил его как правильный ответ, поскольку то, что я размышляю, выходит за рамки моего первоначального вопроса. – MikeJansen

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