In application development, efficiency is not just a goal; it's a necessity. As .NET developers, we often encounter scenarios where we must juggle multiple operations simultaneously. The TaskListProcessor class addresses these issues with efficiency, handling multiple asynchronous operations with grace and precision.
Development Challenges
The TaskListProcessor addresses common challenges in .NET concurrent programming, providing a structured approach to running concurrent tasks with different return types.
Issue: Diverse Return Types
A common issue with concurrent async methods in .NET is handling different return types. Traditional approaches often lead to tangled code, where task management becomes cumbersome and error-prone.
Issue: Error Propagation
Without proper structure, errors from individual tasks can propagate and cause widespread failures. This necessitates a robust mechanism to encapsulate errors and handle them gracefully.
Solution: TaskListProcessor
The TaskListProcessor class addresses these issues head-on, providing a cohesive way to manage a list of tasks regardless of their return types, with built-in error handling and logging.
Benefit: Enhanced Performance
Features methods like WhenAllWithLoggingAsync, which enhance the standard Task.WhenAll with error oversight, flexibility, and scalability using generics and TaskResult objects.
Task.WhenAll vs. Parallel Methods

The choice between using Task.WhenAll and Parallel methods can be effectively illustrated through the metaphor of runners on a track and a tug-of-war contest.
Understanding these differences will help us appreciate the benefits of the TaskListProcessor and select the right approach for your concurrent processing needs.
Task.WhenAll as Runners
Imagine runners, each in their own lane on a track. This represents Task.WhenAll for handling asynchronous, I/O-bound tasks. Each task runs independently without blocking others, ensuring efficiency for network requests or file I/O operations.
Parallel Methods as Tug-of-War
A tug-of-war contest represents Parallel methods for CPU-bound tasks. Teams work together with synchronized effort, similar to how parallel processing distributes computational weight across multiple threads for maximum CPU utilization.
Use Case: Travel Website Dashboard

Consider a travel website displaying a dashboard of top destination cities, aggregating data like weather, attractions, events, and flights from multiple sources.
Performance Challenges
- Diverse data sources with different response times
- Inconsistent data retrieval complexity
TaskListProcessor Solution
- Concurrent data retrieval for faster loading
- Robust error handling prevents cascade failures
Business Benefits
- Responsive dashboard with partial data display
- Maintains user engagement and trust
Technical Jargon Explained
Understanding these technical concepts will help you grasp how TaskListProcessor enhances concurrent task management in .NET applications.
Concurrent Asynchronous Tasks
Tasks executed simultaneously, each operating independently and completing at its own pace. Essential in multi-threading environments for optimal performance.
Error Propagation
The process where errors spread from one system part to others, potentially causing wider failures. Effective error handling prevents this cascade effect.
Telemetry
Collection and analysis of system performance data, including metrics like task execution time and error rates for monitoring and optimization.
ILogger & TaskResult
ILogger: .NET interface for logging events. TaskResult: Custom object encapsulating task outcomes, storing results, names, and error details.
The WhenAllWithLoggingAsync Method
The WhenAllWithLoggingAsync method enhances the standard Task.WhenAll with robust error handling and centralized logging capabilities.
public static async Task WhenAllWithLoggingAsync(IEnumerable<Task> tasks, ILogger logger)
{
ArgumentNullException.ThrowIfNull(logger);
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
logger.LogError(ex, "TLP: An error occurred while executing one or more tasks.");
}
}
Enhanced Error Handling
Instead of allowing exceptions to propagate and potentially crash the application, it catches exceptions and logs them for debugging and analysis.
Consolidated Logging
Centralized logging of task exceptions with consistent formatting, essential for integrating with logging solutions and services.
Non-Blocking Operation
Logs errors internally and allows program continuation, beneficial for non-critical tasks that shouldn't block overall process.
Improved Maintenance
Detailed error information aids in faster debugging and simplifies maintenance in complex systems with many concurrent tasks.
The GetTaskResultAsync Method
The GetTaskResultAsync method wraps async calls with telemetry features, measuring execution time and providing performance metrics.
public async Task GetTaskResultAsync<T>(string taskName, Task<T> task) where T : class
{
var sw = new Stopwatch();
sw.Start();
var taskResult = new TaskResult { Name = taskName };
try
{
taskResult.Data = await task;
sw.Stop();
Telemetry.Add(GetTelemetry(taskName, sw.ElapsedMilliseconds));
}
catch (Exception ex)
{
sw.Stop();
Telemetry.Add(GetTelemetry(taskName, sw.ElapsedMilliseconds, "Exception", ex.Message));
taskResult.Data = null;
}
finally
{
TaskResults.Add(taskResult);
}
}
Performance Metrics
Utilizes Stopwatch to measure and record task execution time, providing valuable performance insights for optimization.
Error Tracking
Captures exceptions during task execution, logging errors with task names and elapsed time for comprehensive analysis.
Execution Isolation
Each task executes in a separate logical block, allowing independent handling where one task failure doesn't impede others.
Generic Flexibility
Designed to return various object types from different tasks within a single list, enabling heterogeneous task processing.
TaskResult Class Overview
The TaskResult class is a cornerstone within the TaskListProcessor architecture, designed to encapsulate the outcome of asynchronous tasks with unified structure.
public class TaskResult<T> : ITaskResult
{
public TaskResult()
{
Name = "UNKNOWN";
Data = null;
}
public TaskResult(string name, T data)
{
Name = name;
Data = data;
}
public T? Data { get; set; }
public string Name { get; set; }
}
Purpose & Standardization
Offers a standardized object representing any task outcome, regardless of nature or return data type, promoting consistency across applications.
Generic Flexibility
Thanks to generic design, can hold any result data type, making it versatile across projects and scenarios for capturing task execution results.
Error Handling
When tasks fail, stores error details alongside original task information, making it invaluable for error tracking and debugging processes.
Telemetry Integration
Can be extended to include telemetry data like execution duration, crucial for performance monitoring and optimization in complex systems.
WeatherService Implementation
The WeatherService class simulates real-world external service calls with artificial latency and potential failures for comprehensive testing scenarios.
Real-World Simulation
Mimics external service calls by introducing artificial latency using Random class, simulating network or service delays developers encounter in production.
Randomized Error Injection
Deliberately throws exceptions based on random conditions to simulate real service failures, ensuring applications handle intermittent failures gracefully.
Adjustable Failure Rate
Customizable failure likelihood provides flexibility in error introduction frequency, allowing thorough testing under various stress and instability conditions.
Realistic Testing
Incorporates randomness in latency and failures for realistic testing environments, ensuring applications handle both success scenarios and unexpected delays.
Travel Dashboard Demo

Let's examine a practical implementation that demonstrates fetching weather forecasts for multiple cities concurrently, simulating a real-world travel dashboard.
Concurrent Data Retrieval
Asynchronous programming makes non-blocking calls to WeatherService for each city, demonstrating parallel data loading for reduced total wait time.
Console Logging Demo
Console.WriteLine outputs progress and results, providing clear sequential logs that mimic how users see data loading on web dashboards.
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<Program>();
var weatherService = new WeatherService();
var weatherCities = new TaskListProcessing.TaskListProcessor();
var cities = new List<string> { "London", "Paris", "New York", "Tokyo", "Sydney", "Chicago", "Dallas", "Wichita" };
var tasks = new List<Task>();
foreach (var city in cities)
{
tasks.Add(weatherCities.GetTaskResultAsync(city, weatherService.GetWeather(city)));
}
await TaskListProcessing.TaskListProcessor.WhenAllWithLoggingAsync(tasks, logger);
Sample Output
Telemetry:
Chicago: Task completed in 602 ms with ERROR Exception: Random failure occurred fetching weather data.
Paris: Task completed in 723 ms with ERROR Exception: Random failure occurred fetching weather data.
Dallas: Task completed in 1,009 ms
Sydney: Task completed in 1,318 ms
Tokyo: Task completed in 1,921 ms
London: Task completed in 2,789 ms
Results:
Dallas: City: Dallas, Date: 2023-11-10, Temp (F): 40, Summary: Cool
Sydney: City: Sydney, Date: 2023-11-10, Temp (F): 116, Summary: Sweltering
Tokyo: City: Tokyo, Date: 2023-11-10, Temp (F): 75, Summary: Warm
London: City: London, Date: 2023-11-10, Temp (F): 16, Summary: Chilly
Enhanced Implementation: CityThingsToDo Service

The latest enhancement introduces the CityThingsToDo service, showcasing the power and flexibility of our architecture to seamlessly integrate different service types and return types.
Architecture Benefits
This enhancement demonstrates how TaskListProcessor handles diverse data sources, reflecting real-world scenarios like travel dashboards that provide weather forecasts and activities for different cities with uniform error handling and telemetry.
var thingsToDoService = new CityThingsToDoService();
var weatherService = new WeatherService();
var cityDashboards = new TaskListProcessorGeneric();
var cities = new List<string> { "London", "Paris", "New York", "Tokyo", "Sydney", "Chicago", "Dallas", "Wichita" };
var tasks = new List<Task>();
foreach (var city in cities)
{
tasks.Add(cityDashboards.GetTaskResultAsync($"{city} Weather", weatherService.GetWeather(city)));
tasks.Add(cityDashboards.GetTaskResultAsync($"{city} Things To Do", thingsToDoService.GetThingsToDoAsync(city)));
}
await cityDashboards.WhenAllWithLoggingAsync(tasks, logger);
Take TaskListProcessor for a Test Drive
In the realm of .NET, orchestrating concurrent asynchronous tasks is a common challenge with the potential to become a transformative learning journey.
Test Drive Experience
Clone the repository and experience the power of TaskListProcessor firsthand. A practical step in the lifelong learning journey for .NET developers.
View on GitHubLearning Outcomes
Master concurrent operations, understand error handling strategies, and implement performance monitoring in your .NET applications.
Version Compatibility
TaskListProcessor is developed for .NET 9, offering the latest framework features and optimizations. Regular updates ensure compatibility with new .NET releases and performance enhancements.
Follow our GitHub repository for the latest updates and access to the most current version with ongoing improvements and community contributions.