Разница между "ToListAsync()" и "AsAsyncEnumerable().ToList()"

Функцию нужно вернуть Task<List<Record>>После оба варианта возвращаются Task<List<Record>>какой из них более эффективен? Есть ли здесь какой-нибудь стандартный способ?

Опция 1:

Task<List<Record>> GetRecords()
{
    return 
    DbContext.Set<Record>.Where(predicate).ToListAsync();
}

Вариант 2:

Task<List<Record>> GetRecords()
{
    return
    DbContext.Set<Record>.Where(predicate).AsAsyncEnumerable().ToList();
}

3 ответа

Решение

Перейти на вариант 1 ToListAsync в качестве исходного кода AsAsyncEnumerable прямо упоминает

Это внутренний API, который поддерживает инфраструктуру Entity Framework Core и не подчиняется тем же стандартам совместимости, что и публичные API. Он может быть изменен или удален без предварительного уведомления в любом выпуске. Вы должны использовать его непосредственно в своем коде с особой осторожностью, зная, что это может привести к сбоям приложения при обновлении до новой версии Entity Framework Core.

Официальная документация упоминает

Этот API поддерживает инфраструктуру Entity Framework Core и не предназначен для использования непосредственно из вашего кода. Этот API может измениться или быть удален в будущих выпусках.

На самом деле никто не объяснил разницу между и .

извлечет все результаты запроса в память, а затем вернет набор результатов как одинList<T>объект. Он будет делать это асинхронно, чтобы не блокировать ввод-вывод, но ничего не вернет , пока не будут доступны все результаты.

AsAsyncEnumerable()будет «давать» каждый результат по мере его доступности. В примере OP они просто звонятToList(), так что результат тот же. Однако, начиная с C# 8.0, вы также можете использовать «асинхронный поток», напримерawait foreach, а затем воздействовать на каждую сущность по мере ее возврата. Например:

      await foreach (var entity in DbContext.Set<Record>.Where(predicate).AsAsyncEnumerable())
{
    // do something with entity...
}

Что касается того, что «лучше», это зависит от вашего варианта использования. Если вы просто возвращаете вызывающему абоненту несколько записей,ToListAsync()все должно быть в порядке.

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

В этом случае, вероятно, лучше использовать асинхронный поток. Это может быть ваш собственныйasync IAsyncEnumerable<T>метод, которыйyieldсвои собственные результаты. Затем вы можете связать их вместе, если это необходимо.

Фактически, начиная с ASP.NET Core 6 , вы можете вернутьIAsyncEnumerable<T>непосредственно из вашего метода контроллера, и он будет "поставлять" эти результаты обратно вызывающей стороне.

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

Хотя существующий ответ pfx по-прежнему верен для.NET Core 2.x и ранее, AsAsyncEnumerableофициально добавлен в.NET Core 3.x. См. Комментарий Яна Кемпа для получения дополнительной информации.

Другие вопросы по тегам