ASP.NET Core now supports async streaming from controller actions all the way down to the response JSON formatter. Returning an IAsyncEnumerable
from an action no longer buffers the response content in memory before it gets sent. This helps reduce memory usage when returning large datasets that can be asynchronously enumerated.
Since DbSet
implements IAsyncEnumerable
, the following example is fully supported:
[ApiController]
public class Controller : ControllerBase
{
private readonly DbContext _context;
public Controller(DbContext context) => _context = context;
[HttpGet]
public IAsyncEnumerable<AspNetUser> GetData()
{
return _context.AspNetUsers.AsAsyncEnumerable();
}
}
Seemingly this feature can only be utilized when returning the data source itself, however when the data source is wrapped, so that an additional metadata could be sent, streaming is not possible.
Basic API result:
public class DataSourceResult<T>
{
public IEnumerable<T> Data { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
}
As many of my applications are running on low-memory environments, I'm looking for an idea how to utilize async streaming in such cases where data is indirectly returned.
CodePudding user response:
All you need to do is change your DataSourceResult<T>
class to have a Data
property type of IAsyncEnumerable<T>
. The System.Text.Json
serialiser recognises the type and streams it to the client. So:
public class DataSourceResult<T>
{
public IAsyncEnumerable<T> Data { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalCount { get; set; }
}
And your action would be something like this:
public DataSourceResult<AspNetUser> GetData()
{
return new DataSourceResult<AspNetUser>
{
Data = _context.AspNetUsers.AsAsyncEnumerable(),
PageIndex = 1,
PageSize = 10,
TotalCount = 12345
};
}