Returning async stream of query results

asp.net-web-api, async-await, c#, ravendb

Solution

It wasn't that hard after all. The solution was a formatter that could process the enumerator asynchronously and write JSON to the stream:

public class CustomJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
    public override async Task WriteToStreamAsync(
           Type type, object value, Stream writeStream, HttpContent content,
           TransportContext transportContext, CancellationToken cancellationToken)
    {
        if (type.IsGenericType &&
            type.GetGenericTypeDefinition() == typeof(IAsyncEnumerator<>))
        {
            var writer = new JsonTextWriter(new StreamWriter(writeStream))
                         { CloseOutput = false };
            writer.WriteStartArray();
            await Serialize((dynamic)value, writer);
            writer.WriteEndArray();
            writer.Flush();
        }
        else
            await base.WriteToStreamAsync(type, value, writeStream, content,
                                          transportContext, cancellationToken);
    }

    async Task Serialize<T>(IAsyncEnumerator<StreamResult<T>> enumerator,
                            JsonTextWriter writer)
    {
        var serializer = JsonSerializer.Create(SerializerSettings);
        while (await enumerator.MoveNextAsync())
            serializer.Serialize(writer, enumerator.Current.Document);
    }
}

Now my WebApi method is even shorter than before:

public Task<IAsyncEnumerator<StreamResult<Foo>>> Get()
{
    var query = AsyncDocumentSession.Query<Foo, FooIndex>();
    return AsyncDocumentSession.Advanced.StreamAsync(query);
}

Problem

I have the following WebApi method that returns an unbounded result stream from RavenDB: ``` public IEnumerable<Foo> Get() { var query = DocumentSession.Query<Foo, FooIndex>(); using (var enumerator = DocumentSession.Advanced.Stream(query)) while (enumerator.MoveNext()) yield return enumerator.Current.Document; } ``` Now I'd like to make that async. The naive approach of course doesn't work: ``` public async Task<IEnumerable<Location>> Get() { var query = AsyncDocumentSession.Query<Foo, FooIndex>(); using (var enumerator = await AsyncDocumentSession.Advanced.StreamAsync(query)) while (await enumerator.MoveNextAsync()) yield return enumerator.Current.Document; } ``` ...because the method can't be both async and an iterator.

Original source

Related problems