#c# #asp.net-core-6.0
Вопрос:
У меня есть действие контроллера, которое возвращает значение IAsyncEnumerable. Он запрашивает источник данных, и если данные доступны, каждый результат «возвращает доходность»(сериализуется System.Text.Json и возвращен клиенту). Но как я могу установить код состояния на 404, если результаты недоступны? Прямо сейчас, если результаты недоступны, метод возвращает пустой массив JSON с кодом состояния = 200.
public async IAsyncEnumerable<string> GetStrings()
{
IEnumerable<string> stringResults = await myData.GetStringsAsync();
if (stringResults is object amp;amp; stringResults.Any())
{
foreach (string result in stringResults)
{
yield return resu<
}
}
else
{
return NotFound(); //CS1622: Cannot return a value from an iterator....
}
}
Обновить
Я хочу, чтобы эта потоковая передача была передана клиенту, как только появятся результаты. Если я вернусь Task<IEnumerable<string>>
, то весь список/массив/коллекция должны быть заполнены до передачи в System.Text.Json для сериализации. Я не думаю, что это очень эффективно для памяти.
Комментарии:
1. @Общее, если оно равно нулю?
2.
IAsyncEnumerable
не сделает синхронный метод асинхронным. Почему вы им пользуетесь?3. Я хочу, чтобы эта потоковая передача была передана клиенту, как только появятся результаты. Если я возвращаю задачу<IEnumerable<строка><строка>>, то весь список/массив/коллекция должны быть заполнены перед передачей в System.Text. Json для сериализации. Я не думаю, что это очень эффективно для памяти.
4.
async Task<IActionResult> GetStrings() ... return Ok(stringResults);
однако вызов.Any()
приведет к тому, что коллекция будет перечислена дважды.5. Если
await myData.GetStringsAsync
возвращаетсяIEnumerable
, действительно ли это возвращает aList
? В каком случае у вас уже есть все результаты? Или этот метод передает результаты синхронно?
Ответ №1:
Вы не можете смешивать асинхронный генератор и возвращаемое действие mvc NotFound()
. Но вы можете написать два метода.
public async Task<IActionResult> GetStrings()
{
IEnumerable<string> stringResults = await myData.GetStringsAsync();
if (stringResults == null)
return NotFound();
var e = stringResults.GetEnumerator();
if (!e.MoveNext())
return NotFound();
return Ok(AsEnum(e));
async IAsyncEnumerable<string> AsEnum(IEnumerator<string> e){
do {
yield return e.Current;
} while (e.MoveNext());
}
}
С любой дополнительной обработкой ошибок и удалением перечислителя.
Однако здесь все еще есть разница в поведении. В отличие async Task
от метода, IEnumerable
IAsyncEnumerable
методы amp; generator не запускаются до первого вызова .MoveNext[Async]
.
Поскольку ваша предыдущая реализация возвращала IAsyncEnumerable
бы значение в MVC перед выполнением любого из ваших действий. Заголовки HTTP будут записаны , и сериализатор json вызовет IAsyncEnumerable.MoveNextAsync
их еще до GetStringAsync
того, как будет вызвана ваша служба.
Если вы хотите дождаться первого результата, прежде чем возвращать 200/404, вам придется подождать, пока серверная служба вернет что-то, прежде чем вы сможете написать какие-либо заголовки HTTP.
Использование времени до первого байта в качестве показателя здесь полностью вводит в заблуждение.
Комментарии:
1. Спасибо за ответ. К сожалению, это все равно приводит к задержке TTFB (т. Е. не потоковой передаче). Также я думаю, что вы имели в виду IActionResult вместо IAsyncResult. Таким образом, похоже, что если я захочу установить код состояния, то я не смогу транслировать.
2. Как мы уже говорили, если ваши исходные данные на самом деле не передаются в потоковом режиме, вы можете не заметить, передаются ли выходные данные в потоковом режиме. Является ли транспортная кодировка ответа фрагментированной? В то время как ваш предыдущий пример, возможно, сразу же запустил заголовки ответов и » [ » , прежде чем вы смогли определить, были ли результаты пустыми. Фактические результаты были бы написаны в аналогичное время. Таким образом, ваш TTFB может вводить в заблуждение.