Introduction
In our previous post, we introduced IAsyncEnumerable for efficient data handling in C#. Now, let’s apply this concept to Web APIs in ASP.NET Core, enabling streaming responses for improved performance and reduced memory usage.
Implementation
ASP.NET Core supports returning IAsyncEnumerable directly from API endpoints. Here’s a basic implementation:
[HttpGet]
public async IAsyncEnumerable<WeatherForecast> GetWeatherStream(
[FromQuery] int days = 30,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var rng = new Random();
for (int i = 0; i < days; i++)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(100, cancellationToken); // Simulate API delay
yield return new WeatherForecast
{
Date = DateTime.Now.AddDays(i),
TemperatureC = rng.Next(-20, 55),
Summary = GetRandomSummary(rng)
};
}
}
private static string GetRandomSummary(Random rng)
{
string[] summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
return summaries[rng.Next(summaries.Length)];
}
This endpoint streams WeatherForecast objects, simulating a delay between each item. The [EnumeratorCancellation] attribute ensures proper cancellation support.
Consuming the Streaming API
Clients can consume this API using HttpClient. Here’s an example:
public async Task ConsumeStreamingApi()
{
using var client = new HttpClient();
var stream = await client.GetStreamAsync("https://api.example.com/streaming?days=10");
await foreach (var forecast in JsonSerializer.DeserializeAsyncEnumerable<WeatherForecast>(stream))
{
Console.WriteLine($"Date: {forecast.Date}, Temp: {forecast.TemperatureC}°C, Summary: {forecast.Summary}");
}
}
This approach allows processing of data as it arrives, rather than waiting for the entire response.
Benefits of Streaming APIs
- Reduced Memory Usage: The server doesn’t need to hold the entire dataset in memory before sending the response.
- Improved Responsiveness: Clients can start processing data immediately, enhancing perceived performance.
- Long-Running Queries: Ideal for scenarios where generating the full response takes significant time.
Best Practices
- Use Cancellation Tokens: Always support cancellation to allow clients to terminate long-running requests.
- Proper Error Handling: Implement try-catch blocks to manage exceptions during streaming.
- Consider Response Size: For very large datasets, implement pagination or limit the maximum number of items.
Conclusion
Implementing streaming Web APIs with IAsyncEnumerable in ASP.NET Core offers a powerful way to handle large datasets efficiently. By streaming data, you can significantly reduce memory usage and improve the responsiveness of your APIs.