Asp.Net Core Controller'dan IAsyncEnumerable <T> ve NotFound öğelerini döndürme


10

A IAsyncEnumerable<T>ve a döndüren NotFoundResultancak yine de zaman uyumsuz bir şekilde işlenen bir denetleyici eylemi için doğru imza nedir ?

Bu imzayı kullandım ve derlenmedi çünkü IAsyncEnumerable<T>beklenmiyor:

[HttpGet]
public async Task<IActionResult> GetAll(Guid id)
{
    try
    {
        return Ok(await repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
    }
    catch (NotFoundException e)
    {
        return NotFound(e.Message);
    }
}

Bu iyi derler ama imzası zaman uyumsuz değildir. Bu yüzden iş parçacığı havuzu konuları engelleyip engellemeyeceği konusunda endişeliyim:

[HttpGet]
public IActionResult GetAll(Guid id)
{
    try
    {
        return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable
    }
    catch (NotFoundException e)
    {
        return NotFound(e.Message);
    }
}

await foreachBöyle bir döngü kullanarak denedim ama açıkçası ya da derlemek olmaz:

[HttpGet]
public async IAsyncEnumerable<MyObject> GetAll(Guid id)
{
    IAsyncEnumerable<MyObject> objects;
    try
    {
        objects = contentDeliveryManagementService.GetAll(id); // GetAll() returns an IAsyncEnumerable
    }
    catch (DeviceNotFoundException e)
    {
        return NotFound(e.Message);
    }

    await foreach (var obj in objects)
    {
        yield return obj;
    }
}

5
MyObjectAynı ürünle birden fazla ürün idmi iade ediyorsunuz ? Normalde NotFound, döndüren bir şey için a göndermezsiniz IEnumerable- yalnızca boş olur - veya tek öğeyi istenen id/ ile döndürürsünüz NotFound.
crgolden

1
IAsyncEnumerablebeklenebilir. Kullanın await foreach(var item from ThatMethodAsync()){...}.
Panagiotis Kanavos

Geri dönmek isterseniz IAsyncEnumerable<MyObject>, sonucu döndürmeniz yeterlidir, örn return objects. Bu alışkanlık olsa akışı gRPC veya SignalR yöntemine bir HTTP eylemi dönüştürün. Ara yazılım hala verileri tüketecek ve istemciye tek bir HTTP yanıtı gönderecektir
Panagiotis Kanavos

Seçenek 2 iyidir. ASP.NET Core sıhhi tesisat numaralandırma ile ilgilenir ve IAsyncEnumerable3.0'dan beri geçerlidir.
Kirk Larkin

Teşekkürler beyler. Burada 404'ümün olmadığını biliyorum, ama bu sadece tartışmalı bir örnek. Gerçek kod oldukça farklı. @KirkLarkin zararlı olduğu için üzgünüm, ancak bunun herhangi bir engellemeye neden olmayacağından% 100 emin misiniz? Evet ise, Seçenek 2 bariz çözümdür.
Frederick The Fool

Yanıtlar:


6

Bir uygulamasını geçer Seçenek 2, IAsyncEnumerable<>içine Okçağrı, gayet iyi. ASP.NET Core sıhhi tesisat numaralandırma ile ilgilenir ve IAsyncEnumerable<>3.0'dan beri geçerlidir.

Bağlam için tekrarlanan sorudan çağrı:

return Ok(repository.GetAll(id)); // GetAll() returns an IAsyncEnumerable

Çağrı için Okbir örneğini oluşturur OkObjectResulthangi devralır ObjectResult. Geçirilen değer Oktiptedir objecttutulur, ObjectResultbireyin Valueözelliği. ASP.NET Core MVC, komutun bir uygulaması olduğu IActionResultve bir uygulaması kullanılarak yürütüldüğü komut desenini kullanır IActionResultExecutor<T>.

İçin ObjectResult, HTTP yanıtına dönüştürmek için ObjectResultExecutorkullanılır ObjectResult. Bu var uygulama içinde ObjectResultExecutor.ExecuteAsyncolmasıdır IAsyncEnumerable<>-uyumlu:

public virtual Task ExecuteAsync(ActionContext context, ObjectResult result)
{
    // ...

    var value = result.Value;

    if (value != null && _asyncEnumerableReaderFactory.TryGetReader(value.GetType(), out var reader))
    {
        return ExecuteAsyncEnumerable(context, result, value, reader);
    }

    return ExecuteAsyncCore(context, result, objectType, value);
}

Kodda görüldüğü gibi, Valueözellik uygulanıp uygulanmadığını kontrol eder IAsyncEnumerable<>(ayrıntılar çağrıda gizlidir TryGetReader). Bunu ExecuteAsyncEnumerableyaparsa, numaralandırmayı gerçekleştiren ve sonra numaralandırılan sonucu geçiren denir ExecuteAsyncCore:

private async Task ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, object asyncEnumerable, Func<object, Task<ICollection>> reader)
{
    Log.BufferingAsyncEnumerable(Logger, asyncEnumerable);

    var enumerated = await reader(asyncEnumerable);
    await ExecuteAsyncCore(context, result, enumerated.GetType(), enumerated);
}

readerYukarıdaki pasajda numaralandırma gerçekleşir. Biraz gömülü, ama kaynağı burada görebilirsiniz :

private async Task<ICollection> ReadInternal<T>(object value)
{
    var asyncEnumerable = (IAsyncEnumerable<T>)value;
    var result = new List<T>();
    var count = 0;

    await foreach (var item in asyncEnumerable)
    {
        if (count++ >= _mvcOptions.MaxIAsyncEnumerableBufferLimit)
        {
            throw new InvalidOperationException(Resources.FormatObjectResultExecutor_MaxEnumerationExceeded(
                nameof(AsyncEnumerableReader),
                value.GetType()));
        }

        result.Add(item);
    }

    return result;
}

IAsyncEnumerable<>Bir halinde numaralandırılmış List<>kullanılarak await foreachneredeyse tanım olarak, bir istek iplik engellemeyecek olan,. Panagiotis Kanavos'un OP üzerine yaptığı bir yorumda söylediği gibi, bu numaralandırma müşteriye bir yanıt gönderilmeden önce tam olarak gerçekleştirilir .


Ayrıntılı cevap için teşekkürler Kirk :). Sahip olduğum bir endişe, seçenek 2'deki eylem yönteminin kendisidir. Dönüş değerinin eşzamansız olarak numaralandırılacağını anladım , ancak bir Tasknesneyi döndürmedi . Bu gerçeğin kendisi asenkronikliği herhangi bir şekilde engelliyor mu? Özellikle benzer bir yöntemle karşılaştırıldığında, a Task.
Frederick The Fool

1
Hayır, sorun değil. A değerini döndürmek için bir neden yoktur Task, çünkü yöntemin kendisi eşzamansız bir çalışma gerçekleştirmez. Yukarıda açıklandığı gibi işlenen asenkron olan numaralandırmadır. Bunu görebilirsiniz Taskyürütülmesinde, orada kullanılır ObjectResult.
Kirk Larkin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.