A long running problem in ASP.NET has been the inability to handle returning large JSON files without consuming a lot of memory. The default behavior for the framework is to buffer the entire output at once, convert it en masse to JSON, and then start feeding the results to the client. This can lead to out-of-memory situations if the amount of data is large enough.
The inability to stream results was never a fundamental problem for ASP.NET. When working with simpler formats such as CSV, the developer has always had the ability write directly to the result stream.
Starting with ASP.NET Core 5, the Utf8JsonWriter class can be used for the same effect. The code has a few complexities to it, such as the need to explicitly disable buffering, but can work if you follow the instructions provided by Alexander Vasilyev.
ASP.NET Core 6 makes this easier by directly supporting IAsyncEnumerable. If an IAsyncEnumerable
is returned by a function, the framework will interpret it as a request to stream the data to the client asynchronously without buffering.
One way to get an IAsyncEnumerable
is to use Entity Framework Core. This has actually been a feature since EF Core 3, but its utility in web servers has been limited until now. ASP.NET Core didn’t support IAsyncEnumerable
results until version 5, and even then it would still buffer the results.
In ASP.NET Core Announcements #463, developers were informed that the new IAsyncEnumerable
should be considered a breaking change.
In most cases, the absence of buffering would not be observed by the application. However, some scenarios may have inadvertently relied on the buffering semantics to correctly serialize. For instance, returning an
IAsyncEnumerable<>
that is backed by a EF query on a type with lazy loaded properties might result in concurrent query execution which might be unsupported by the provider.
In order to revert back to the previous behavior, developers need to explicitly call ToListAsync()
. Alternately, they can disable behaviors such as lazy loading.