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.