ASP.NET Core  

ASP.NET Core Error Handling: Master Middleware, Logging & Exception Strategies (Part-20 of 40)

image

Table of Contents

  1. The Philosophy of Error Handling

  2. Understanding Exception Types in .NET

  3. Basic Try-Catch Patterns

  4. Global Error Handling with Middleware

  5. Custom Exception Middleware

  6. Exception Filters in MVC

  7. Structured Logging with Serilog

  8. Error Monitoring & Alerting

  9. API Error Responses & Standards

  10. Validation Error Handling

  11. Database & ORM Error Handling

  12. Async/Await Error Patterns

  13. Custom Exception Classes

  14. Error Handling in Background Services

  15. Health Checks & Diagnostics

  16. Performance Considerations

  17. Security Aspects of Error Handling

  18. Testing Error Scenarios

  19. Real-World Implementation Example

  20. Conclusion & Best Practices

1. The Philosophy of Error Handling

Error handling is not just about catching exceptions—it's about creating resilient, maintainable applications that provide meaningful feedback to users and developers. In modern web applications, proper error handling is crucial for:

  • User Experience: Providing clear, helpful error messages

  • Debugging: Capturing sufficient context for issue resolution

  • Monitoring: Tracking application health and error trends

  • Security: Preventing information leakage through error messages

Real-World Analogy: Air Traffic Control

Think of error handling like air traffic control:

  • Try-Catch: Pilot's immediate reaction to turbulence

  • Middleware: Air traffic control's monitoring system

  • Logging: Flight data recorder (black box)

  • Global Handling: Emergency procedures and protocols

2. Understanding Exception Types in .NET

Common Exception Hierarchy

  
    // Base exception types
public void DemonstrateExceptionTypes()
{
    try
    {
        // System.Exception - The root of all exceptions
        throw new Exception("This is a generic exception");
        
        // System.SystemException - Base class for system exceptions
        throw new SystemException("System-level exception occurred");
        
        // System.ApplicationException - Base class for application exceptions
        throw new ApplicationException("Application-specific error");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Caught exception: {ex.GetType().Name}");
    }
}
  

Specific Exception Types with Real-World Context

  
    public class BankAccountService
{
    private readonly Dictionary<string, decimal> _accounts = new();

    public void TransferMoney(string fromAccount, string toAccount, decimal amount)
    {
        try
        {
            // ArgumentException - Invalid input parameters
            if (string.IsNullOrEmpty(fromAccount))
                throw new ArgumentException("Source account cannot be empty", nameof(fromAccount));
            
            if (string.IsNullOrEmpty(toAccount))
                throw new ArgumentException("Destination account cannot be empty", nameof(toAccount));
            
            if (amount <= 0)
                throw new ArgumentOutOfRangeException(nameof(amount), "Transfer amount must be positive");

            // InvalidOperationException - Object in invalid state
            if (!_accounts.ContainsKey(fromAccount))
                throw new InvalidOperationException($"Source account {fromAccount} does not exist");
            
            if (!_accounts.ContainsKey(toAccount))
                throw new InvalidOperationException($"Destination account {toAccount} does not exist");

            // Custom business logic exception
            if (_accounts[fromAccount] < amount)
                throw new InsufficientFundsException(fromAccount, amount, _accounts[fromAccount]);

            // Perform transfer
            _accounts[fromAccount] -= amount;
            _accounts[toAccount] += amount;
        }
        catch (ArgumentException ex)
        {
            // Log and rethrow with additional context
            throw new BankingOperationException(
                "Invalid parameters provided for money transfer", ex);
        }
    }
}

// Custom business exception
public class InsufficientFundsException : Exception
{
    public string AccountNumber { get; }
    public decimal RequestedAmount { get; }
    public decimal CurrentBalance { get; }

    public InsufficientFundsException(string accountNumber, decimal requestedAmount, decimal currentBalance)
        : base($"Insufficient funds in account {accountNumber}. " +
               $"Requested: {requestedAmount:C}, Available: {currentBalance:C}")
    {
        AccountNumber = accountNumber;
        RequestedAmount = requestedAmount;
        CurrentBalance = currentBalance;
    }
}
  

3. Basic Try-Catch Patterns <a name="try-catch-patterns"></a>

Fundamental Try-Catch-Finally

  
    public class FileProcessor
{
    public string ReadFileContent(string filePath)
    {
        FileStream fileStream = null;
        StreamReader reader = null;
        
        try
        {
            // Attempt to open and read file
            fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            reader = new StreamReader(fileStream);
            return reader.ReadToEnd();
        }
        catch (FileNotFoundException ex)
        {
            // Specific handling for file not found
            throw new ProcessingException($"File not found: {filePath}", ex);
        }
        catch (DirectoryNotFoundException ex)
        {
            // Specific handling for directory not found
            throw new ProcessingException($"Directory not found for file: {filePath}", ex);
        }
        catch (IOException ex) when (ex.Message.Contains("sharing violation"))
        {
            // Filtered catch for file locking issues
            throw new ProcessingException($"File is locked by another process: {filePath}", ex);
        }
        catch (Exception ex)
        {
            // General exception handling
            throw new ProcessingException($"Error reading file: {filePath}", ex);
        }
        finally
        {
            // Always execute - cleanup resources
            reader?.Dispose();
            fileStream?.Dispose();
        }
    }
}
  

Modern Using Statements with Error Handling

  
    public async Task<string> ReadFileAsync(string filePath)
{
    try
    {
        // Using declaration automatically disposes the resource
        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        using var reader = new StreamReader(fileStream);
        
        return await reader.ReadToEndAsync();
    }
    catch (Exception ex) when (IsFileAccessError(ex))
    {
        // Filter based on custom condition
        throw new ProcessingException($"File access error: {filePath}", ex);
    }
}

private bool IsFileAccessError(Exception ex)
{
    return ex is FileNotFoundException || 
           ex is DirectoryNotFoundException ||
           ex is UnauthorizedAccessException;
}
  

Exception Filters in Action

  
    public class PaymentProcessor
{
    public async Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
    {
        try
        {
            // Payment processing logic
            var result = await _paymentGateway.ProcessAsync(request);
            return result;
        }
        catch (HttpRequestException ex) when (ex.Message.Contains("timeout", StringComparison.OrdinalIgnoreCase))
        {
            // Handle timeout specifically
            throw new PaymentTimeoutException("Payment gateway timeout", ex);
        }
        catch (HttpRequestException ex) when ((int?)ex.StatusCode >= 500)
        {
            // Handle server errors from payment gateway
            throw new PaymentGatewayException("Payment gateway server error", ex);
        }
        catch (HttpRequestException ex) when ((int?)ex.StatusCode == 401)
        {
            // Handle authentication errors
            throw new PaymentAuthenticationException("Invalid payment gateway credentials", ex);
        }
    }
}
  

4. Global Error Handling with Middleware <a name="global-error-handling"></a>

Built-in Exception Handling Middleware

  
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Development environment error handling
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage(); // For Entity Framework errors
    }
    else
    {
        // Production environment error handling
        app.UseExceptionHandler("/error");
        app.UseStatusCodePagesWithReExecute("/error/{0}");
        
        // HSTS for security
        app.UseHsts();
    }

    // Custom error handling middleware
    app.UseMiddleware<GlobalExceptionMiddleware>();
    
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}
  

Custom Error Handling Endpoint

  
    [ApiController]
public class ErrorController : ControllerBase
{
    private readonly IWebHostEnvironment _environment;

    public ErrorController(IWebHostEnvironment environment)
    {
        _environment = environment;
    }

    [Route("/error")]
    public IActionResult HandleError()
    {
        var exceptionHandlerPathFeature = 
            HttpContext.Features.Get<IExceptionHandlerPathFeature>();
        
        var exception = exceptionHandlerPathFeature?.Error;
        var path = exceptionHandlerPathFeature?.Path;

        // Log the error here
        _logger.LogError(exception, "Unhandled exception occurred for path: {Path}", path);

        if (_environment.IsDevelopment())
        {
            return Problem(
                detail: exception?.ToString(),
                instance: path,
                title: exception?.Message);
        }

        // Production response
        return Problem(
            detail: "An error occurred while processing your request.",
            instance: path,
            title: "Internal Server Error");
    }

    [Route("/error/{statusCode}")]
    public IActionResult HandleError(int statusCode)
    {
        var statusCodeReExecuteFeature = 
            HttpContext.Features.Get<IStatusCodeReExecuteFeature>();

        var path = statusCodeReExecuteFeature?.OriginalPath;

        _logger.LogWarning("HTTP {StatusCode} occurred for path: {Path}", statusCode, path);

        var problemDetails = new ProblemDetails
        {
            Status = statusCode,
            Title = GetStatusCodeTitle(statusCode),
            Instance = path,
            Detail = GetStatusCodeDetail(statusCode)
        };

        return StatusCode(statusCode, problemDetails);
    }

    private static string GetStatusCodeTitle(int statusCode) => statusCode switch
    {
        400 => "Bad Request",
        401 => "Unauthorized",
        403 => "Forbidden",
        404 => "Not Found",
        500 => "Internal Server Error",
        _ => "Error"
    };

    private static string GetStatusCodeDetail(int statusCode) => statusCode switch
    {
        400 => "The request was malformed or contained invalid parameters.",
        401 => "Authentication is required to access this resource.",
        403 => "You don't have permission to access this resource.",
        404 => "The requested resource was not found.",
        500 => "An internal server error occurred.",
        _ => "An error occurred."
    };
}
  

5. Custom Exception Middleware <a name="custom-middleware"></a>

Comprehensive Global Exception Middleware

  
    public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;
    private readonly IWebHostEnvironment _environment;

    public GlobalExceptionMiddleware(
        RequestDelegate next, 
        ILogger<GlobalExceptionMiddleware> logger,
        IWebHostEnvironment environment)
    {
        _next = next;
        _logger = logger;
        _environment = environment;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        // Log the exception with structured logging
        LogException(exception, context);

        // Determine the response based on exception type
        var (statusCode, problemDetails) = CreateProblemDetails(exception, context);

        // Set response properties
        context.Response.StatusCode = statusCode;
        context.Response.ContentType = "application/problem+json";

        // Serialize and return the problem details
        var json = JsonSerializer.Serialize(problemDetails, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = _environment.IsDevelopment()
        });

        await context.Response.WriteAsync(json);
    }

    private void LogException(Exception exception, HttpContext context)
    {
        var logLevel = GetLogLevel(exception);
        var eventId = new EventId(exception.HResult);
        var request = context.Request;

        _logger.Log(
            logLevel,
            eventId,
            exception,
            "Unhandled exception occurred. " +
            "Method: {Method}, Path: {Path}, QueryString: {QueryString}",
            request.Method,
            request.Path,
            request.QueryString);
    }

    private static LogLevel GetLogLevel(Exception exception) => exception switch
    {
        ValidationException => LogLevel.Warning,
        NotFoundException => LogLevel.Warning,
        UnauthorizedAccessException => LogLevel.Warning,
        _ => LogLevel.Error
    };

    private (int StatusCode, ProblemDetails ProblemDetails) CreateProblemDetails(
        Exception exception, HttpContext context)
    {
        var statusCode = exception switch
        {
            ValidationException => StatusCodes.Status400BadRequest,
            NotFoundException => StatusCodes.Status404NotFound,
            UnauthorizedAccessException => StatusCodes.Status401Unauthorized,
            BusinessRuleException => StatusCodes.Status422UnprocessableEntity,
            _ => StatusCodes.Status500InternalServerError
        };

        var problemDetails = new ProblemDetails
        {
            Status = statusCode,
            Title = GetExceptionTitle(exception),
            Detail = GetExceptionDetail(exception),
            Instance = context.Request.Path,
            Type = GetExceptionType(exception)
        };

        // Add additional information for specific exception types
        if (exception is ValidationException validationException)
        {
            problemDetails.Extensions["errors"] = validationException.Errors;
        }

        if (exception is BusinessRuleException businessException)
        {
            problemDetails.Extensions["rule"] = businessException.RuleName;
            problemDetails.Extensions["businessContext"] = businessException.BusinessContext;
        }

        // Include stack trace in development
        if (_environment.IsDevelopment())
        {
            problemDetails.Extensions["trace"] = exception.StackTrace;
            problemDetails.Extensions["innerException"] = exception.InnerException?.Message;
        }

        return (statusCode, problemDetails);
    }

    private static string GetExceptionTitle(Exception exception) => exception switch
    {
        ValidationException => "Validation Error",
        NotFoundException => "Resource Not Found",
        UnauthorizedAccessException => "Access Denied",
        BusinessRuleException => "Business Rule Violation",
        _ => "Internal Server Error"
    };

    private string GetExceptionDetail(Exception exception) => exception switch
    {
        ValidationException validationEx => validationEx.Message,
        NotFoundException notFoundEx => notFoundEx.Message,
        UnauthorizedAccessException => "You don't have permission to access this resource.",
        BusinessRuleException businessEx => businessEx.Message,
        _ => _environment.IsDevelopment() 
            ? exception.Message 
            : "An error occurred while processing your request."
    };

    private static string GetExceptionType(Exception exception) => exception switch
    {
        ValidationException => "https://tools.ietf.org/html/rfc7231#section-6.5.1",
        NotFoundException => "https://tools.ietf.org/html/rfc7231#section-6.5.4",
        UnauthorizedAccessException => "https://tools.ietf.org/html/rfc7235#section-3.1",
        BusinessRuleException => "https://tools.ietf.org/html/rfc4918#section-11.2",
        _ => "https://tools.ietf.org/html/rfc7231#section-6.6.1"
    };
}
  

Advanced Exception Middleware with Correlation IDs

  
    public class CorrelationIdMiddleware
{
    private readonly RequestDelegate _next;
    private const string CorrelationIdHeader = "X-Correlation-ID";

    public CorrelationIdMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var correlationId = GetOrCreateCorrelationId(context);
        context.Items["CorrelationId"] = correlationId;
        context.Response.Headers[CorrelationIdHeader] = correlationId;

        // Add correlation ID to logger scope
        using (_logger.BeginScope("{CorrelationId}", correlationId))
        {
            await _next(context);
        }
    }

    private static string GetOrCreateCorrelationId(HttpContext context)
    {
        if (context.Request.Headers.TryGetValue(CorrelationIdHeader, out var correlationId))
        {
            return correlationId.ToString();
        }

        return Guid.NewGuid().ToString();
    }
}

// Enhanced exception middleware with correlation ID
public class EnhancedExceptionMiddleware
{
    // ... previous code ...

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        var correlationId = context.Items["CorrelationId"] as string ?? "unknown";

        // Log with correlation ID
        _logger.LogError(
            exception, 
            "Unhandled exception with correlation ID: {CorrelationId}", 
            correlationId);

        var problemDetails = CreateProblemDetails(exception, context, correlationId);
        
        context.Response.StatusCode = problemDetails.Status ?? 500;
        context.Response.ContentType = "application/problem+json";

        var json = JsonSerializer.Serialize(problemDetails);
        await context.Response.WriteAsync(json);
    }

    private ProblemDetails CreateProblemDetails(Exception exception, HttpContext context, string correlationId)
    {
        var problemDetails = new ProblemDetails
        {
            Status = GetStatusCode(exception),
            Title = GetExceptionTitle(exception),
            Detail = GetExceptionDetail(exception),
            Instance = context.Request.Path,
            Extensions = { ["correlationId"] = correlationId }
        };

        // Add machine name for debugging
        problemDetails.Extensions["machineName"] = Environment.MachineName;
        
        // Add timestamp
        problemDetails.Extensions["timestamp"] = DateTime.UtcNow.ToString("O");

        return problemDetails;
    }
}
  

6. Exception Filters in MVC <a name="exception-filters"></a>

Global Exception Filter

  
    public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly ILogger<GlobalExceptionFilter> _logger;
    private readonly IWebHostEnvironment _environment;

    public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger, IWebHostEnvironment environment)
    {
        _logger = logger;
        _environment = environment;
    }

    public void OnException(ExceptionContext context)
    {
        // Only handle if exception isn't already handled
        if (context.ExceptionHandled) return;

        var exception = context.Exception;
        var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;

        LogException(exception, actionDescriptor);

        var result = CreateErrorResult(exception, actionDescriptor);
        
        context.Result = result;
        context.ExceptionHandled = true;
    }

    private void LogException(Exception exception, ControllerActionDescriptor actionDescriptor)
    {
        var logLevel = GetLogLevel(exception);
        
        _logger.Log(
            logLevel,
            exception,
            "Exception in {Controller}.{Action}",
            actionDescriptor?.ControllerName,
            actionDescriptor?.ActionName);
    }

    private IActionResult CreateErrorResult(Exception exception, ControllerActionDescriptor actionDescriptor)
    {
        // API controllers return ProblemDetails
        if (IsApiController(actionDescriptor))
        {
            return CreateProblemDetailsResult(exception);
        }

        // MVC controllers return error views
        return CreateViewResult(exception);
    }

    private bool IsApiController(ControllerActionDescriptor actionDescriptor)
    {
        return actionDescriptor?.ControllerTypeInfo
            .GetCustomAttributes<ApiControllerAttribute>()
            .Any() == true;
    }

    private IActionResult CreateProblemDetailsResult(Exception exception)
    {
        var statusCode = GetStatusCode(exception);
        var problemDetails = new ProblemDetails
        {
            Status = statusCode,
            Title = GetExceptionTitle(exception),
            Detail = _environment.IsDevelopment() ? exception.Message : null,
            Type = GetExceptionType(exception)
        };

        if (exception is ValidationException validationException)
        {
            problemDetails.Extensions["errors"] = validationException.Errors;
        }

        return new ObjectResult(problemDetails)
        {
            StatusCode = statusCode
        };
    }

    private IActionResult CreateViewResult(Exception exception)
    {
        var statusCode = GetStatusCode(exception);
        var viewName = statusCode switch
        {
            404 => "NotFound",
            403 => "AccessDenied",
            _ => "Error"
        };

        var model = new ErrorViewModel
        {
            RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier,
            Message = _environment.IsDevelopment() ? exception.Message : "An error occurred.",
            StackTrace = _environment.IsDevelopment() ? exception.StackTrace : null
        };

        return new ViewResult
        {
            ViewName = viewName,
            StatusCode = statusCode,
            ViewData = new ViewDataDictionary<ErrorViewModel>(ViewData, model)
        };
    }
}
  

Action-Specific Exception Filter

  
    public class HandleBusinessExceptionAttribute : ExceptionFilterAttribute
{
    private readonly string _errorView;
    private readonly string _logMessage;

    public HandleBusinessExceptionAttribute(string errorView = "BusinessError", string logMessage = null)
    {
        _errorView = errorView;
        _logMessage = logMessage;
    }

    public override void OnException(ExceptionContext context)
    {
        if (context.Exception is BusinessRuleException businessException)
        {
            // Log the business exception
            if (!string.IsNullOrEmpty(_logMessage))
            {
                context.HttpContext.RequestServices
                    .GetRequiredService<ILogger<HandleBusinessExceptionAttribute>>()
                    .LogWarning(businessException, _logMessage);
            }

            // Handle the exception
            context.ExceptionHandled = true;
            
            if (IsApiController(context))
            {
                context.Result = new ObjectResult(new
                {
                    error = businessException.Message,
                    rule = businessException.RuleName,
                    context = businessException.BusinessContext
                })
                {
                    StatusCode = 422 // Unprocessable Entity
                };
            }
            else
            {
                context.Result = new ViewResult
                {
                    ViewName = _errorView,
                    ViewData = new ViewDataDictionary(new Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary())
                    {
                        Model = new BusinessErrorViewModel
                        {
                            Message = businessException.Message,
                            RuleName = businessException.RuleName,
                            BusinessContext = businessException.BusinessContext
                        }
                    }
                };
            }
        }
    }

    private static bool IsApiController(ExceptionContext context)
    {
        return context.ActionDescriptor is ControllerActionDescriptor descriptor &&
               descriptor.ControllerTypeInfo.GetCustomAttributes<ApiControllerAttribute>().Any();
    }
}

// Usage in controllers
[ApiController]
[Route("api/[controller]")]
[HandleBusinessException]
public class OrdersController : ControllerBase
{
    [HttpPost]
    [HandleBusinessException("OrderError", "Business rule violation in order creation")]
    public async Task<IActionResult> CreateOrder(CreateOrderRequest request)
    {
        // Business logic that might throw BusinessRuleException
        var order = await _orderService.CreateOrderAsync(request);
        return Ok(order);
    }
}
  

7. Structured Logging with Serilog <a name="structured-logging"></a>

Comprehensive Serilog Configuration

  
    public static class LoggingConfiguration
{
    public static IHostBuilder ConfigureSerilog(this IHostBuilder hostBuilder)
    {
        return hostBuilder.UseSerilog((context, services, configuration) =>
        {
            var env = context.HostingEnvironment;
            
            configuration
                .ReadFrom.Configuration(context.Configuration)
                .ReadFrom.Services(services)
                .Enrich.FromLogContext()
                .Enrich.WithMachineName()
                .Enrich.WithEnvironmentName()
                .Enrich.WithProperty("Application", context.HostingEnvironment.ApplicationName)
                .Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName)
                .Enrich.WithExceptionDetails()
                .WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} " +
                                   "{Properties:j}{NewLine}{Exception}")
                .WriteTo.Debug()
                .WriteTo.File(
                    path: "Logs/log-.txt",
                    rollingInterval: RollingInterval.Day,
                    retainedFileCountLimit: 7,
                    shared: true,
                    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
                .WriteTo.Seq(serverUrl: context.Configuration.GetValue<string>("Seq:ServerUrl"))
                .WriteTo.ApplicationInsights(
                    services.GetRequiredService<TelemetryConfiguration>(),
                    TelemetryConverter.Traces);

            // Add additional sinks based on environment
            if (env.IsProduction())
            {
                configuration.WriteTo.AzureAnalytics(
                    workspaceId: context.Configuration["AzureLogAnalytics:WorkspaceId"],
                    authenticationId: context.Configuration["AzureLogAnalytics:AuthenticationId"]);
            }
        });
    }
}
  

Structured Logging in Action

  
    public class OrderService
{
    private readonly ILogger<OrderService> _logger;

    public OrderService(ILogger<OrderService> logger)
    {
        _logger = logger;
    }

    public async Task<Order> ProcessOrderAsync(OrderRequest request)
    {
        using var activity = _logger.BeginScope(new Dictionary<string, object>
        {
            ["OrderId"] = request.OrderId,
            ["CustomerId"] = request.CustomerId,
            ["TotalAmount"] = request.TotalAmount
        });

        try
        {
            _logger.LogInformation(
                "Starting order processing for order {OrderId} from customer {CustomerId}",
                request.OrderId,
                request.CustomerId);

            // Validate order
            if (!await ValidateOrderAsync(request))
            {
                _logger.LogWarning(
                    "Order validation failed for order {OrderId}. Customer: {CustomerId}",
                    request.OrderId,
                    request.CustomerId);
                
                throw new ValidationException("Order validation failed");
            }

            // Process payment
            var paymentResult = await ProcessPaymentAsync(request);
            
            _logger.LogInformation(
                "Payment processed successfully for order {OrderId}. " +
                "PaymentId: {PaymentId}, Amount: {Amount}",
                request.OrderId,
                paymentResult.PaymentId,
                paymentResult.Amount);

            // Create order
            var order = await CreateOrderAsync(request, paymentResult);
            
            _logger.LogInformation(
                "Order created successfully. OrderId: {OrderId}, Status: {Status}",
                order.Id,
                order.Status);

            return order;
        }
        catch (PaymentException ex)
        {
            _logger.LogError(
                ex,
                "Payment processing failed for order {OrderId}. " +
                "Error: {ErrorMessage}, PaymentGateway: {Gateway}",
                request.OrderId,
                ex.Message,
                ex.GatewayName);
            
            throw;
        }
        catch (Exception ex)
        {
            _logger.LogError(
                ex,
                "Unexpected error processing order {OrderId}. " +
                "Customer: {CustomerId}, Amount: {Amount}",
                request.OrderId,
                request.CustomerId,
                request.TotalAmount);
            
            throw new OrderProcessingException("Failed to process order", ex);
        }
    }

    public async Task RetryOrderProcessingAsync(Order order)
    {
        var stopwatch = Stopwatch.StartNew();
        
        try
        {
            _logger.LogInformation("Retrying order processing for order {OrderId}", order.Id);
            
            await _orderProcessor.RetryAsync(order);
            
            _logger.LogInformation(
                "Order retry completed successfully. OrderId: {OrderId}, Duration: {ElapsedMs}ms",
                order.Id,
                stopwatch.ElapsedMilliseconds);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(
                ex,
                "Order retry failed. OrderId: {OrderId}, Attempt: {Attempt}, Duration: {ElapsedMs}ms",
                order.Id,
                order.RetryCount,
                stopwatch.ElapsedMilliseconds);
            
            throw;
        }
        finally
        {
            stopwatch.Stop();
        }
    }
}
  

Advanced Logging Patterns

  
    public static class LoggerExtensions
{
    // Extension methods for specific log scenarios
    public static void LogSecurityEvent(this ILogger logger, 
        string eventType, string userId, string resource, string details)
    {
        logger.LogWarning(
            "Security event: {EventType}. User: {UserId}, Resource: {Resource}, Details: {Details}",
            eventType, userId, resource, details);
    }

    public static void LogPerformanceMetric(this ILogger logger,
        string operation, long durationMs, string context = null)
    {
        var logLevel = durationMs > 1000 ? LogLevel.Warning : LogLevel.Information;
        
        logger.Log(
            logLevel,
            "Performance metric: {Operation} took {DurationMs}ms. Context: {Context}",
            operation, durationMs, context);
    }

    public static void LogBusinessEvent(this ILogger logger,
        string eventType, string entityType, string entityId, object additionalData = null)
    {
        logger.LogInformation(
            "Business event: {EventType}. Entity: {EntityType}, ID: {EntityId}, Data: {@AdditionalData}",
            eventType, entityType, entityId, additionalData);
    }

    // High-performance logging with conditional compilation
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void LogDebugIfEnabled(this ILogger logger, string message, params object[] args)
    {
        if (logger.IsEnabled(LogLevel.Debug))
        {
            logger.LogDebug(message, args);
        }
    }
}

// Usage
_logger.LogSecurityEvent("LoginFailed", user.Id, "Authentication", "Invalid credentials");
_logger.LogPerformanceMetric("DatabaseQuery", 150, "GetUserOrders");
_logger.LogBusinessEvent("OrderCreated", "Order", order.Id, new { order.TotalAmount, order.Currency });
  

8. Error Monitoring & Alerting <a name="error-monitoring"></a>

Application Insights Integration

  
    public static class ApplicationInsightsConfig
{
    public static void ConfigureApplicationInsights(IServiceCollection services, IConfiguration configuration)
    {
        services.AddApplicationInsightsTelemetry(options =>
        {
            options.ConnectionString = configuration["ApplicationInsights:ConnectionString"];
            options.EnableAdaptiveSampling = false; // Get all telemetry for errors
            options.EnableDependencyTrackingTelemetryModule = true;
            options.EnablePerformanceCounterCollectionModule = true;
            options.EnableRequestTrackingTelemetryModule = true;
            options.EnableExceptionTrackingTelemetryModule = true;
        });

        // Custom telemetry initializer for additional context
        services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
        
        // Telemetry processor for filtering
        services.AddApplicationInsightsTelemetryProcessor<FilterHealthCheckTelemetryProcessor>();
    }
}

public class CustomTelemetryInitializer : ITelemetryInitializer
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        var context = _httpContextAccessor.HttpContext;
        
        if (context == null) return;

        // Add correlation ID
        if (context.Items.TryGetValue("CorrelationId", out var correlationId))
        {
            telemetry.Context.Operation.Id = correlationId.ToString();
        }

        // Add user information
        if (context.User.Identity.IsAuthenticated)
        {
            telemetry.Context.User.Id = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            telemetry.Context.User.AuthenticatedUserId = context.User.Identity.Name;
        }

        // Add custom properties
        telemetry.Context.Cloud.RoleName = "OrderProcessingService";
        telemetry.Context.Component.Version = Assembly.GetEntryAssembly()?.GetName().Version?.ToString();
        
        // Add business context for request telemetry
        if (telemetry is RequestTelemetry requestTelemetry)
        {
            if (context.Request.RouteValues.TryGetValue("controller", out var controller))
            {
                requestTelemetry.Properties["Controller"] = controller.ToString();
            }
            
            if (context.Request.RouteValues.TryGetValue("action", out var action))
            {
                requestTelemetry.Properties["Action"] = action.ToString();
            }
        }
    }
}

public class FilterHealthCheckTelemetryProcessor : ITelemetryProcessor
{
    private readonly ITelemetryProcessor _next;

    public FilterHealthCheckTelemetryProcessor(ITelemetryProcessor next)
    {
        _next = next;
    }

    public void Process(ITelemetry item)
    {
        // Filter out health check requests to reduce noise
        if (item is RequestTelemetry request && 
            request.Url != null && 
            request.Url.AbsolutePath.Contains("/health"))
        {
            return;
        }

        _next.Process(item);
    }
}
  

Custom Error Monitoring Service

  
    public interface IErrorMonitoringService
{
    Task TrackExceptionAsync(Exception exception, IDictionary<string, object> context = null);
    Task TrackErrorAsync(string errorCode, string message, IDictionary<string, object> context = null);
    Task<bool> ShouldAlertAsync(Exception exception);
}

public class ErrorMonitoringService : IErrorMonitoringService
{
    private readonly TelemetryClient _telemetryClient;
    private readonly ILogger<ErrorMonitoringService> _logger;
    private readonly IConfiguration _configuration;

    public ErrorMonitoringService(
        TelemetryClient telemetryClient,
        ILogger<ErrorMonitoringService> logger,
        IConfiguration configuration)
    {
        _telemetryClient = telemetryClient;
        _logger = logger;
        _configuration = configuration;
    }

    public async Task TrackExceptionAsync(Exception exception, IDictionary<string, object> context = null)
    {
        try
        {
            var telemetry = new ExceptionTelemetry(exception);
            
            // Add context to telemetry
            if (context != null)
            {
                foreach (var kvp in context)
                {
                    telemetry.Properties[kvp.Key] = kvp.Value?.ToString();
                }
            }

            // Track the exception
            _telemetryClient.TrackException(telemetry);
            
            // Flush to ensure telemetry is sent
            _telemetryClient.Flush();
            
            // Check if we should send an alert
            if (await ShouldAlertAsync(exception))
            {
                await SendAlertAsync(exception, context);
            }

            _logger.LogInformation("Exception tracked: {ExceptionType}", exception.GetType().Name);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to track exception in monitoring service");
        }
    }

    public async Task TrackErrorAsync(string errorCode, string message, IDictionary<string, object> context = null)
    {
        var telemetry = new TraceTelemetry(message, SeverityLevel.Error);
        telemetry.Properties["ErrorCode"] = errorCode;
        
        if (context != null)
        {
            foreach (var kvp in context)
            {
                telemetry.Properties[kvp.Key] = kvp.Value?.ToString();
            }
        }

        _telemetryClient.TrackTrace(telemetry);
        _telemetryClient.Flush();
        
        _logger.LogWarning("Error tracked: {ErrorCode} - {Message}", errorCode, message);
    }

    public Task<bool> ShouldAlertAsync(Exception exception)
    {
        // Don't alert for client errors
        if (exception is ValidationException || 
            exception is NotFoundException ||
            exception is UnauthorizedAccessException)
        {
            return Task.FromResult(false);
        }

        // Alert for critical infrastructure errors
        if (exception is DatabaseConnectionException ||
            exception is ExternalServiceException ||
            exception is OutOfMemoryException)
        {
            return Task.FromResult(true);
        }

        // Rate limiting: don't alert for the same error repeatedly
        return Task.FromResult(true);
    }

    private async Task SendAlertAsync(Exception exception, IDictionary<string, object> context)
    {
        try
        {
            var alertSettings = _configuration.GetSection("ErrorAlerting");
            var enabled = alertSettings.GetValue<bool>("Enabled");
            
            if (!enabled) return;

            var alertMessage = CreateAlertMessage(exception, context);
            
            // Send alert via configured channels (email, Slack, etc.)
            await SendAlertToChannelsAsync(alertMessage);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send error alert");
        }
    }

    private string CreateAlertMessage(Exception exception, IDictionary<string, object> context)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"🚨 APPLICATION ALERT: {exception.GetType().Name}");
        sb.AppendLine($"Message: {exception.Message}");
        sb.AppendLine($"Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
        
        if (context != null && context.Any())
        {
            sb.AppendLine("Context:");
            foreach (var kvp in context)
            {
                sb.AppendLine($"  {kvp.Key}: {kvp.Value}");
            }
        }

        if (exception.StackTrace != null)
        {
            sb.AppendLine($"Stack Trace: {exception.StackTrace}");
        }

        return sb.ToString();
    }

    private async Task SendAlertToChannelsAsync(string alertMessage)
    {
        var alertChannels = _configuration.GetSection("ErrorAlerting:Channels").Get<string[]>();
        
        foreach (var channel in alertChannels ?? Array.Empty<string>())
        {
            try
            {
                switch (channel.ToLowerInvariant())
                {
                    case "email":
                        await SendEmailAlertAsync(alertMessage);
                        break;
                    case "slack":
                        await SendSlackAlertAsync(alertMessage);
                        break;
                    case "teams":
                        await SendTeamsAlertAsync(alertMessage);
                        break;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send alert to channel: {Channel}", channel);
            }
        }
    }

    private Task SendEmailAlertAsync(string alertMessage)
    {
        // Implementation for email alerts
        return Task.CompletedTask;
    }

    private Task SendSlackAlertAsync(string alertMessage)
    {
        // Implementation for Slack alerts
        return Task.CompletedTask;
    }

    private Task SendTeamsAlertAsync(string alertMessage)
    {
        // Implementation for Microsoft Teams alerts
        return Task.CompletedTask;
    }
}
  

9. API Error Responses & Standards <a name="api-error-responses"></a>

RFC 7807 Problem Details Implementation

  
    public class ProblemDetailsService : IProblemDetailsService
{
    private readonly IWebHostEnvironment _environment;
    private readonly ILogger<ProblemDetailsService> _logger;

    public ProblemDetailsService(IWebHostEnvironment environment, ILogger<ProblemDetailsService> logger)
    {
        _environment = environment;
        _logger = logger;
    }

    public async ValueTask WriteAsync(ProblemDetailsContext context)
    {
        var httpContext = context.HttpContext;
        var problemDetails = context.ProblemDetails;

        // Ensure status code is set
        problemDetails.Status ??= httpContext.Response.StatusCode;

        // Set instance if not provided
        problemDetails.Instance ??= httpContext.Request.Path;

        // Add correlation ID
        if (httpContext.Items.TryGetValue("CorrelationId", out var correlationId))
        {
            problemDetails.Extensions["correlationId"] = correlationId;
        }

        // Add trace ID for debugging
        problemDetails.Extensions["traceId"] = Activity.Current?.Id ?? httpContext.TraceIdentifier;

        // Include additional details in development
        if (_environment.IsDevelopment())
        {
            AddDevelopmentDetails(problemDetails, context.Exception);
        }

        // Log the problem
        LogProblemDetails(problemDetails, context.Exception, httpContext);

        // Write the response
        httpContext.Response.StatusCode = problemDetails.Status.Value;
        httpContext.Response.ContentType = "application/problem+json";

        var options = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
        };

        await httpContext.Response.WriteAsJsonAsync(problemDetails, options, context.HttpContext.RequestAborted);
    }

    private void AddDevelopmentDetails(ProblemDetails problemDetails, Exception exception)
    {
        if (exception != null)
        {
            problemDetails.Extensions["exception"] = new
            {
                type = exception.GetType().FullName,
                message = exception.Message,
                stackTrace = exception.StackTrace,
                innerException = exception.InnerException?.Message
            };
        }

        problemDetails.Extensions["machineName"] = Environment.MachineName;
        problemDetails.Extensions["timestamp"] = DateTime.UtcNow.ToString("O");
    }

    private void LogProblemDetails(ProblemDetails problemDetails, Exception exception, HttpContext httpContext)
    {
        var logLevel = GetLogLevel(problemDetails.Status.Value, exception);

        _logger.Log(
            logLevel,
            exception,
            "Problem details response. Status: {StatusCode}, Type: {ProblemType}, " +
            "Title: {Title}, Instance: {Instance}, CorrelationId: {CorrelationId}",
            problemDetails.Status,
            problemDetails.Type,
            problemDetails.Title,
            problemDetails.Instance,
            problemDetails.Extensions.GetValueOrDefault("correlationId"));
    }

    private static LogLevel GetLogLevel(int statusCode, Exception exception)
    {
        if (exception != null) return LogLevel.Error;
        
        return statusCode switch
        {
            >= 500 => LogLevel.Error,
            >= 400 => LogLevel.Warning,
            _ => LogLevel.Information
        };
    }
}

// Custom problem details for specific scenarios
public class ValidationProblemDetails : ProblemDetails
{
    public IDictionary<string, string[]> Errors { get; set; } = new Dictionary<string, string[]>();

    public ValidationProblemDetails()
    {
        Title = "Validation Error";
        Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1";
    }

    public ValidationProblemDetails(ModelStateDictionary modelState) : this()
    {
        if (modelState == null)
            throw new ArgumentNullException(nameof(modelState));

        foreach (var keyModelStatePair in modelState)
        {
            var key = keyModelStatePair.Key;
            var errors = keyModelStatePair.Value.Errors;
            
            if (errors != null && errors.Count > 0)
            {
                if (errors.Count == 1)
                {
                    var errorMessage = GetErrorMessage(errors[0]);
                    Errors.Add(key, new[] { errorMessage });
                }
                else
                {
                    var errorMessages = new string[errors.Count];
                    for (var i = 0; i < errors.Count; i++)
                    {
                        errorMessages[i] = GetErrorMessage(errors[i]);
                    }

                    Errors.Add(key, errorMessages);
                }
            }
        }
    }

    private static string GetErrorMessage(ModelError error)
    {
        return string.IsNullOrEmpty(error.ErrorMessage) 
            ? "The input was not valid." 
            : error.ErrorMessage;
    }
}

public class BusinessRuleProblemDetails : ProblemDetails
{
    public string RuleName { get; set; }
    public string BusinessContext { get; set; }
    public string SuggestedAction { get; set; }

    public BusinessRuleProblemDetails(BusinessRuleException exception)
    {
        Title = "Business Rule Violation";
        Status = StatusCodes.Status422UnprocessableEntity;
        Type = "https://tools.ietf.org/html/rfc4918#section-11.2";
        Detail = exception.Message;
        RuleName = exception.RuleName;
        BusinessContext = exception.BusinessContext;
        SuggestedAction = GetSuggestedAction(exception.RuleName);
    }

    private static string GetSuggestedAction(string ruleName)
    {
        return ruleName switch
        {
            "InsufficientInventory" => "Reduce the quantity or check back later",
            "PriceMismatch" => "Refresh the product and try again",
            "ShippingRestriction" => "Choose a different shipping method or address",
            _ => "Please review your request and try again"
        };
    }
}
  

API-Specific Exception Handling

  
    [ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
[Consumes("application/json")]
public class ApiControllerBase : ControllerBase
{
    protected readonly ILogger Logger;

    protected ApiControllerBase(ILogger logger)
    {
        Logger = logger;
    }

    protected IActionResult OkOrNotFound<T>(T result, string resourceName = null)
    {
        if (result == null)
        {
            var message = resourceName != null 
                ? $"{resourceName} not found" 
                : "Resource not found";
            
            return NotFound(new ProblemDetails
            {
                Title = "Resource Not Found",
                Status = StatusCodes.Status404NotFound,
                Detail = message,
                Instance = HttpContext.Request.Path
            });
        }

        return Ok(result);
    }

    protected IActionResult CreatedAt<T>(string actionName, object routeValues, T value)
    {
        return CreatedAtAction(actionName, routeValues, value);
    }

    protected async Task<IActionResult> ExecuteAndHandleAsync<T>(
        Func<Task<T>> operation,
        string successLogMessage = null)
    {
        try
        {
            var result = await operation();
            
            if (!string.IsNullOrEmpty(successLogMessage))
            {
                Logger.LogInformation(successLogMessage);
            }

            return Ok(result);
        }
        catch (ValidationException ex)
        {
            Logger.LogWarning(ex, "Validation error in API operation");
            return BadRequest(new ValidationProblemDetails
            {
                Title = "Validation Error",
                Detail = ex.Message,
                Instance = HttpContext.Request.Path,
                Errors = ex.Errors
            });
        }
        catch (NotFoundException ex)
        {
            Logger.LogWarning(ex, "Resource not found in API operation");
            return NotFound(new ProblemDetails
            {
                Title = "Resource Not Found",
                Status = StatusCodes.Status404NotFound,
                Detail = ex.Message,
                Instance = HttpContext.Request.Path
            });
        }
        catch (BusinessRuleException ex)
        {
            Logger.LogWarning(ex, "Business rule violation in API operation");
            return UnprocessableEntity(new BusinessRuleProblemDetails(ex));
        }
        catch (UnauthorizedAccessException ex)
        {
            Logger.LogWarning(ex, "Unauthorized access in API operation");
            return Unauthorized(new ProblemDetails
            {
                Title = "Unauthorized",
                Status = StatusCodes.Status401Unauthorized,
                Detail = "Authentication is required to access this resource",
                Instance = HttpContext.Request.Path
            });
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Unexpected error in API operation");
            return StatusCode(500, new ProblemDetails
            {
                Title = "Internal Server Error",
                Status = StatusCodes.Status500InternalServerError,
                Detail = "An unexpected error occurred",
                Instance = HttpContext.Request.Path
            });
        }
    }
}
  

10. Validation Error Handling <a name="validation-errors"></a>

Comprehensive Validation Framework

  
    public class CustomValidationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            var problemDetails = new ValidationProblemDetails(context.ModelState)
            {
                Status = StatusCodes.Status400BadRequest,
                Instance = context.HttpContext.Request.Path,
                Extensions = { ["traceId"] = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier }
            };

            context.Result = new BadRequestObjectResult(problemDetails);
        }

        base.OnActionExecuting(context);
    }
}

// Custom validation exception
public class ValidationException : Exception
{
    public IDictionary<string, string[]> Errors { get; }

    public ValidationException() : base("One or more validation errors occurred.")
    {
        Errors = new Dictionary<string, string[]>();
    }

    public ValidationException(IDictionary<string, string[]> errors) : this()
    {
        Errors = errors;
    }

    public ValidationException(string message) : base(message)
    {
        Errors = new Dictionary<string, string[]>();
    }

    public ValidationException(string message, IDictionary<string, string[]> errors) : base(message)
    {
        Errors = errors;
    }

    public ValidationException(string message, Exception innerException) : base(message, innerException)
    {
        Errors = new Dictionary<string, string[]>();
    }
}

// FluentValidation integration
public class FluentValidationFilter : IEndpointFilter
{
    private readonly IServiceProvider _serviceProvider;

    public FluentValidationFilter(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
    {
        foreach (var argument in context.Arguments)
        {
            if (argument == null) continue;

            var argumentType = argument.GetType();
            var validatorType = typeof(IValidator<>).MakeGenericType(argumentType);
            var validator = _serviceProvider.GetService(validatorType) as IValidator;

            if (validator != null)
            {
                var validationContext = new ValidationContext<object>(argument);
                var validationResult = await validator.ValidateAsync(validationContext);

                if (!validationResult.IsValid)
                {
                    var errors = validationResult.Errors
                        .GroupBy(x => x.PropertyName)
                        .ToDictionary(
                            g => g.Key,
                            g => g.Select(x => x.ErrorMessage).ToArray());

                    throw new ValidationException("Validation failed", errors);
                }
            }
        }

        return await next(context);
    }
}

// Custom validators
public class CreateOrderRequestValidator : AbstractValidator<CreateOrderRequest>
{
    public CreateOrderRequestValidator()
    {
        RuleFor(x => x.CustomerId)
            .NotEmpty().WithMessage("Customer ID is required")
            .MaximumLength(50).WithMessage("Customer ID cannot exceed 50 characters");

        RuleFor(x => x.Items)
            .NotEmpty().WithMessage("Order must contain at least one item")
            .Must(items => items.Sum(i => i.Quantity) > 0).WithMessage("Total quantity must be greater than 0");

        RuleForEach(x => x.Items).SetValidator(new OrderItemValidator());

        RuleFor(x => x.ShippingAddress)
            .NotNull().WithMessage("Shipping address is required")
            .SetValidator(new AddressValidator());

        RuleFor(x => x.TotalAmount)
            .GreaterThan(0).WithMessage("Total amount must be positive")
            .ScalePrecision(2, 18).WithMessage("Total amount must have valid precision");

        RuleFor(x => x.Currency)
            .NotEmpty().WithMessage("Currency is required")
            .Length(3).WithMessage("Currency must be a 3-letter code")
            .Must(BeValidCurrency).WithMessage("Invalid currency code");
    }

    private bool BeValidCurrency(string currency)
    {
        return new[] { "USD", "EUR", "GBP", "JPY" }.Contains(currency.ToUpperInvariant());
    }
}

public class OrderItemValidator : AbstractValidator<OrderItemRequest>
{
    public OrderItemValidator()
    {
        RuleFor(x => x.ProductId)
            .NotEmpty().WithMessage("Product ID is required");

        RuleFor(x => x.Quantity)
            .GreaterThan(0).WithMessage("Quantity must be positive")
            .LessThanOrEqualTo(1000).WithMessage("Quantity cannot exceed 1000");

        RuleFor(x => x.UnitPrice)
            .GreaterThanOrEqualTo(0).WithMessage("Unit price cannot be negative")
            .ScalePrecision(2, 18).WithMessage("Unit price must have valid precision");
    }
}
  

11. Database & ORM Error Handling <a name="database-errors"></a>

Entity Framework Core Error Handling

  
    public class RepositoryBase<T> where T : class
{
    private readonly ApplicationDbContext _context;
    private readonly ILogger<RepositoryBase<T>> _logger;

    public RepositoryBase(ApplicationDbContext context, ILogger<RepositoryBase<T>> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<T> GetByIdAsync(int id)
    {
        try
        {
            return await _context.Set<T>().FindAsync(id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error retrieving {EntityType} with ID {Id}", typeof(T).Name, id);
            throw new DataAccessException($"Error retrieving {typeof(T).Name}", ex);
        }
    }

    public async Task AddAsync(T entity)
    {
        try
        {
            await _context.Set<T>().AddAsync(entity);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException ex) when (IsUniqueConstraintViolation(ex))
        {
            _logger.LogWarning(ex, "Unique constraint violation while adding {EntityType}", typeof(T).Name);
            throw new UniqueConstraintException($"Duplicate {typeof(T).Name} found", ex);
        }
        catch (DbUpdateException ex) when (IsForeignKeyViolation(ex))
        {
            _logger.LogWarning(ex, "Foreign key violation while adding {EntityType}", typeof(T).Name);
            throw new ForeignKeyViolationException("Related entity not found", ex);
        }
        catch (DbUpdateException ex)
        {
            _logger.LogError(ex, "Database update error while adding {EntityType}", typeof(T).Name);
            throw new DataAccessException($"Error adding {typeof(T).Name}", ex);
        }
    }

    public async Task UpdateAsync(T entity)
    {
        try
        {
            _context.Set<T>().Update(entity);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            _logger.LogWarning(ex, "Concurrency conflict while updating {EntityType}", typeof(T).Name);
            throw new ConcurrencyException("Data was modified by another user", ex);
        }
        catch (DbUpdateException ex)
        {
            _logger.LogError(ex, "Database update error while updating {EntityType}", typeof(T).Name);
            throw new DataAccessException($"Error updating {typeof(T).Name}", ex);
        }
    }

    public async Task ExecuteInTransactionAsync(Func<Task> operation)
    {
        await using var transaction = await _context.Database.BeginTransactionAsync();
        
        try
        {
            await operation();
            await transaction.CommitAsync();
        }
        catch (Exception ex)
        {
            await transaction.RollbackAsync();
            _logger.LogError(ex, "Transaction failed for {EntityType} operation", typeof(T).Name);
            throw;
        }
    }

    private static bool IsUniqueConstraintViolation(DbUpdateException ex)
    {
        return ex.InnerException is SqlException sqlEx && 
               (sqlEx.Number == 2627 || sqlEx.Number == 2601); // Unique constraint violation
    }

    private static bool IsForeignKeyViolation(DbUpdateException ex)
    {
        return ex.InnerException is SqlException sqlEx && sqlEx.Number == 547; // Foreign key violation
    }
}

// Custom database exceptions
public class DataAccessException : Exception
{
    public DataAccessException(string message) : base(message) { }
    public DataAccessException(string message, Exception innerException) : base(message, innerException) { }
}

public class UniqueConstraintException : DataAccessException
{
    public UniqueConstraintException(string message) : base(message) { }
    public UniqueConstraintException(string message, Exception innerException) : base(message, innerException) { }
}

public class ForeignKeyViolationException : DataAccessException
{
    public ForeignKeyViolationException(string message) : base(message) { }
    public ForeignKeyViolationException(string message, Exception innerException) : base(message, innerException) { }
}

public class ConcurrencyException : DataAccessException
{
    public ConcurrencyException(string message) : base(message) { }
    public ConcurrencyException(string message, Exception innerException) : base(message, innerException) { }
}
  

Resilient Database Operations

  
    public class ResilientDatabaseExecutor
{
    private readonly ILogger<ResilientDatabaseExecutor> _logger;
    private readonly IPolicy[] _policies;

    public ResilientDatabaseExecutor(ILogger<ResilientDatabaseExecutor> logger)
    {
        _logger = logger;
        _policies = CreateResiliencePolicies();
    }

    public async Task<T> ExecuteAsync<T>(Func<Task<T>> operation, string operationName)
    {
        var policyWrap = Policy.WrapAsync(_policies);
        
        return await policyWrap.ExecuteAsync(async () =>
        {
            using var activity = _logger.BeginScope(new Dictionary<string, object>
            {
                ["DatabaseOperation"] = operationName,
                ["RetryCount"] = 0 // This would be updated by retry policy
            });

            _logger.LogInformation("Executing database operation: {Operation}", operationName);
            
            return await operation();
        });
    }

    public async Task ExecuteAsync(Func<Task> operation, string operationName)
    {
        var policyWrap = Policy.WrapAsync(_policies);
        
        await policyWrap.ExecuteAsync(async () =>
        {
            using var activity = _logger.BeginScope(new Dictionary<string, object>
            {
                ["DatabaseOperation"] = operationName
            });

            _logger.LogInformation("Executing database operation: {Operation}", operationName);
            
            await operation();
        });
    }

    private IAsyncPolicy[] CreateResiliencePolicies()
    {
        // Retry policy for transient errors
        var retryPolicy = Policy
            .Handle<SqlException>(IsTransientError)
            .Or<TimeoutException>()
            .OrInner<SocketException>()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryCount, context) =>
                {
                    _logger.LogWarning(
                        "Retry {RetryCount} for {Operation} after {Delay}ms. Error: {Error}",
                        retryCount,
                        context.OperationKey,
                        timespan.TotalMilliseconds,
                        outcome.Exception?.Message);
                });

        // Circuit breaker policy
        var circuitBreakerPolicy = Policy
            .Handle<SqlException>(IsTransientError)
            .Or<TimeoutException>()
            .CircuitBreakerAsync(
                exceptionsAllowedBeforeBreaking: 5,
                durationOfBreak: TimeSpan.FromMinutes(1),
                onBreak: (exception, timespan) =>
                {
                    _logger.LogError(
                        "Circuit breaker opened for {Timespan} due to: {Error}",
                        timespan,
                        exception.Message);
                },
                onReset: () =>
                {
                    _logger.LogInformation("Circuit breaker reset");
                },
                onHalfOpen: () =>
                {
                    _logger.LogInformation("Circuit breaker half-open");
                });

        // Timeout policy
        var timeoutPolicy = Policy
            .TimeoutAsync(TimeSpan.FromSeconds(30), onTimeoutAsync: (context, timespan, task) =>
            {
                _logger.LogWarning("Operation {Operation} timed out after {Timeout}s", 
                    context.OperationKey, timespan.TotalSeconds);
                return Task.CompletedTask;
            });

        return new IAsyncPolicy[] { retryPolicy, circuitBreakerPolicy, timeoutPolicy };
    }

    private static bool IsTransientError(SqlException sqlException)
    {
        // SQL Server transient error codes
        var transientErrorNumbers = new[] { -2, 20, 64, 233, 10053, 10054, 10060, 40143, 40197, 40501, 40613 };
        return transientErrorNumbers.Contains(sqlException.Number);
    }
}
  

12. Async/Await Error Patterns <a name="async-error-patterns"></a>

Proper Async Error Handling

  
    public class AsyncOrderProcessor
{
    private readonly ILogger<AsyncOrderProcessor> _logger;
    private readonly IOrderRepository _orderRepository;
    private readonly IPaymentService _paymentService;
    private readonly IInventoryService _inventoryService;

    public AsyncOrderProcessor(
        ILogger<AsyncOrderProcessor> logger,
        IOrderRepository orderRepository,
        IPaymentService paymentService,
        IInventoryService inventoryService)
    {
        _logger = logger;
        _orderRepository = orderRepository;
        _paymentService = paymentService;
        _inventoryService = inventoryService;
    }

    public async Task<OrderProcessingResult> ProcessOrderAsync(OrderRequest request)
    {
        using var activity = _logger.BeginScope(new Dictionary<string, object>
        {
            ["OrderId"] = request.OrderId,
            ["CustomerId"] = request.CustomerId,
            ["Operation"] = "ProcessOrder"
        });

        try
        {
            _logger.LogInformation("Starting order processing for order {OrderId}", request.OrderId);

            // Validate order asynchronously
            await ValidateOrderAsync(request);

            // Process payment asynchronously
            var paymentResult = await ProcessPaymentWithRetryAsync(request);

            // Reserve inventory asynchronously
            await ReserveInventoryAsync(request.Items);

            // Create order in database
            var order = await CreateOrderAsync(request, paymentResult);

            _logger.LogInformation("Order processing completed successfully for order {OrderId}", request.OrderId);

            return new OrderProcessingResult
            {
                Success = true,
                Order = order,
                PaymentId = paymentResult.PaymentId
            };
        }
        catch (Exception ex) when (ex is ValidationException or PaymentException or InventoryException)
        {
            _logger.LogWarning(ex, "Business error processing order {OrderId}", request.OrderId);
            return new OrderProcessingResult
            {
                Success = false,
                Error = ex.Message,
                ErrorType = ex.GetType().Name
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error processing order {OrderId}", request.OrderId);
            throw new OrderProcessingException("Failed to process order", ex);
        }
    }

    private async Task ValidateOrderAsync(OrderRequest request)
    {
        try
        {
            var validationTasks = new[]
            {
                ValidateCustomerAsync(request.CustomerId),
                ValidateProductsAsync(request.Items.Select(i => i.ProductId)),
                ValidateShippingAsync(request.ShippingAddress)
            };

            // Execute all validations concurrently
            await Task.WhenAll(validationTasks);

            _logger.LogDebug("Order validation completed for order {OrderId}", request.OrderId);
        }
        catch (AggregateException ae)
        {
            // Flatten and handle multiple validation errors
            var validationErrors = ae.Flatten().InnerExceptions
                .OfType<ValidationException>()
                .SelectMany(ex => ex.Errors)
                .ToDictionary(pair => pair.Key, pair => pair.Value);

            throw new ValidationException("Order validation failed", validationErrors);
        }
    }

    private async Task<PaymentResult> ProcessPaymentWithRetryAsync(OrderRequest request)
    {
        var retryPolicy = Policy
            .Handle<PaymentException>(ex => ex.IsRetryable)
            .Or<HttpRequestException>()
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (exception, delay, retryCount, context) =>
                {
                    _logger.LogWarning(
                        "Retry {RetryCount} for payment processing after {Delay}ms. Error: {Error}",
                        retryCount,
                        delay.TotalMilliseconds,
                        exception.Message);
                });

        return await retryPolicy.ExecuteAsync(async () =>
        {
            try
            {
                return await _paymentService.ProcessPaymentAsync(request);
            }
            catch (PaymentException ex) when (!ex.IsRetryable)
            {
                _logger.LogError(ex, "Non-retryable payment error for order {OrderId}", request.OrderId);
                throw;
            }
        });
    }

    private async Task ReserveInventoryAsync(IEnumerable<OrderItemRequest> items)
    {
        var reserveTasks = items.Select(item => 
            _inventoryService.ReserveAsync(item.ProductId, item.Quantity));

        var results = await Task.WhenAll(reserveTasks);

        // Check if any reservations failed
        var failedReservations = results.Where(r => !r.Success).ToList();
        if (failedReservations.Any())
        {
            var errorMessages = failedReservations.Select(r => r.ErrorMessage);
            throw new InventoryException($"Inventory reservation failed: {string.Join("; ", errorMessages)}");
        }

        _logger.LogDebug("Inventory reserved successfully for order items");
    }

    // Fire-and-forget with proper error handling
    public void SendOrderConfirmationEmail(Order order)
    {
        // Use Task.Run to avoid blocking, but capture the task
        var fireAndForgetTask = Task.Run(async () =>
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
                
                await emailService.SendOrderConfirmationAsync(order);
                _logger.LogInformation("Order confirmation email sent for order {OrderId}", order.Id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send order confirmation email for order {OrderId}", order.Id);
                // Don't rethrow - this is fire-and-forget
            }
        });

        // Optional: Store the task for potential awaiting later
        // _backgroundTasks.Add(fireAndForgetTask);
    }
}
  

Async Stream Error Handling

  
    public class AsyncStreamProcessor
{
    private readonly ILogger<AsyncStreamProcessor> _logger;

    public AsyncStreamProcessor(ILogger<AsyncStreamProcessor> logger)
    {
        _logger = logger;
    }

    public async IAsyncEnumerable<ProcessedItem> ProcessStreamAsync(
        IAsyncEnumerable<InputItem> inputStream,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        await foreach (var item in inputStream.WithCancellation(cancellationToken))
        {
            ProcessedItem processedItem = null;
            
            try
            {
                processedItem = await ProcessItemAsync(item, cancellationToken);
            }
            catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
            {
                _logger.LogInformation("Stream processing cancelled");
                yield break;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing item {ItemId}", item.Id);
                
                // Yield an error result instead of breaking the stream
                yield return new ProcessedItem
                {
                    Id = item.Id,
                    Success = false,
                    Error = ex.Message
                };
                continue;
            }

            if (processedItem != null)
            {
                yield return processedItem;
            }
        }
    }

    public async Task ProcessBatchesAsync(
        IAsyncEnumerable<InputBatch> batches,
        int maxConcurrency = 5,
        CancellationToken cancellationToken = default)
    {
        var semaphore = new SemaphoreSlim(maxConcurrency);
        var tasks = new List<Task>();

        await foreach (var batch in batches.WithCancellation(cancellationToken))
        {
            await semaphore.WaitAsync(cancellationToken);

            var task = Task.Run(async () =>
            {
                try
                {
                    await ProcessBatchAsync(batch, cancellationToken);
                }
                catch (Exception ex) when (ex is not OperationCanceledException)
                {
                    _logger.LogError(ex, "Error processing batch {BatchId}", batch.Id);
                }
                finally
                {
                    semaphore.Release();
                }
            }, cancellationToken);

            tasks.Add(task);
        }

        // Wait for all tasks to complete
        await Task.WhenAll(tasks);
    }

    private async Task<ProcessedItem> ProcessItemAsync(InputItem item, CancellationToken cancellationToken)
    {
        using var activity = _logger.BeginScope(new Dictionary<string, object>
        {
            ["ItemId"] = item.Id,
            ["ItemType"] = item.Type
        });

        _logger.LogInformation("Processing item {ItemId}", item.Id);

        // Simulate async processing
        await Task.Delay(100, cancellationToken);

        if (item.ShouldFail)
        {
            throw new InvalidOperationException($"Simulated failure for item {item.Id}");
        }

        return new ProcessedItem
        {
            Id = item.Id,
            Success = true,
            ProcessedAt = DateTime.UtcNow
        };
    }
}
  

13. Custom Exception Classes <a name="custom-exceptions"></a>

Comprehensive Custom Exception Hierarchy

  
    // Base custom exception with additional context
public abstract class CustomException : Exception
{
    public string ErrorCode { get; }
    public DateTime Timestamp { get; }
    public string UserFriendlyMessage { get; }
    public IDictionary<string, object> Context { get; }

    protected CustomException(
        string message,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, innerException)
    {
        ErrorCode = errorCode ?? GetDefaultErrorCode();
        Timestamp = DateTime.UtcNow;
        UserFriendlyMessage = userFriendlyMessage ?? message;
        Context = new Dictionary<string, object>();
    }

    public CustomException WithContext(string key, object value)
    {
        Context[key] = value;
        return this;
    }

    protected abstract string GetDefaultErrorCode();
}

// Domain-specific exceptions
public abstract class DomainException : CustomException
{
    protected DomainException(
        string message,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode, userFriendlyMessage, innerException)
    {
    }

    protected override string GetDefaultErrorCode() => "DOMAIN_ERROR";
}

public class BusinessRuleException : DomainException
{
    public string RuleName { get; }
    public string BusinessContext { get; }

    public BusinessRuleException(
        string ruleName,
        string message,
        string businessContext = null,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode ?? "BUSINESS_RULE_VIOLATION", userFriendlyMessage, innerException)
    {
        RuleName = ruleName;
        BusinessContext = businessContext;
    }

    public static BusinessRuleException InsufficientInventory(string productId, int requested, int available)
    {
        return new BusinessRuleException(
            "INSUFFICIENT_INVENTORY",
            $"Insufficient inventory for product {productId}. Requested: {requested}, Available: {available}",
            $"Product: {productId}",
            "INSUFFICIENT_INVENTORY",
            $"Sorry, we only have {available} units of this product in stock.");
    }

    public static BusinessRuleException PriceMismatch(string productId, decimal expected, decimal actual)
    {
        return new BusinessRuleException(
            "PRICE_MISMATCH",
            $"Price mismatch for product {productId}. Expected: {expected:C}, Actual: {actual:C}",
            $"Product: {productId}",
            "PRICE_MISMATCH",
            "The product price has changed. Please refresh the page and try again.");
    }
}

public class ValidationException : DomainException
{
    public IDictionary<string, string[]> Errors { get; }

    public ValidationException(IDictionary<string, string[]> errors)
        : base("Validation failed", "VALIDATION_ERROR", "Please correct the validation errors and try again.")
    {
        Errors = errors ?? new Dictionary<string, string[]>();
    }

    public ValidationException(string property, string error)
        : this(new Dictionary<string, string[]> { [property] = new[] { error } })
    {
    }

    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine(base.ToString());
        
        if (Errors.Any())
        {
            sb.AppendLine("Validation Errors:");
            foreach (var error in Errors)
            {
                sb.AppendLine($"  {error.Key}: {string.Join(", ", error.Value)}");
            }
        }

        return sb.ToString();
    }
}

// Infrastructure exceptions
public abstract class InfrastructureException : CustomException
{
    protected InfrastructureException(
        string message,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode, userFriendlyMessage, innerException)
    {
    }

    protected override string GetDefaultErrorCode() => "INFRASTRUCTURE_ERROR";
}

public class ExternalServiceException : InfrastructureException
{
    public string ServiceName { get; }
    public string Operation { get; }
    public int? StatusCode { get; }

    public ExternalServiceException(
        string serviceName,
        string operation,
        string message,
        int? statusCode = null,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode ?? "EXTERNAL_SERVICE_ERROR", userFriendlyMessage, innerException)
    {
        ServiceName = serviceName;
        Operation = operation;
        StatusCode = statusCode;
    }

    public bool IsTransient => StatusCode >= 500 || StatusCode == 429; // Server error or rate limiting
}

public class DatabaseConnectionException : InfrastructureException
{
    public string DatabaseName { get; }
    public string Server { get; }

    public DatabaseConnectionException(
        string databaseName,
        string server,
        string message,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode ?? "DATABASE_CONNECTION_ERROR", userFriendlyMessage, innerException)
    {
        DatabaseName = databaseName;
        Server = server;
    }
}

// Application-specific exceptions
public class OrderProcessingException : CustomException
{
    public string OrderId { get; }
    public OrderProcessingStep FailedStep { get; }

    public OrderProcessingException(
        string orderId,
        OrderProcessingStep failedStep,
        string message,
        string errorCode = null,
        string userFriendlyMessage = null,
        Exception innerException = null)
        : base(message, errorCode ?? "ORDER_PROCESSING_ERROR", userFriendlyMessage, innerException)
    {
        OrderId = orderId;
        FailedStep = failedStep;
    }

    protected override string GetDefaultErrorCode() => "ORDER_PROCESSING_ERROR";
}

public enum OrderProcessingStep
{
    Validation,
    Payment,
    Inventory,
    Fulfillment,
    Notification
}
  

Exception Factory Pattern

  
    public static class ExceptionFactory
{
    public static ValidationException CreateValidationException(
        string propertyName, 
        string errorMessage,
        string errorCode = null)
    {
        var errors = new Dictionary<string, string[]>
        {
            [propertyName] = new[] { errorMessage }
        };

        return new ValidationException(errors)
        {
            ErrorCode = errorCode ?? "VALIDATION_ERROR"
        };
    }

    public static BusinessRuleException CreateBusinessRuleException(
        string ruleName,
        string message,
        params (string Key, object Value)[] context)
    {
        var exception = new BusinessRuleException(ruleName, message);
        
        foreach (var (key, value) in context)
        {
            exception.WithContext(key, value);
        }

        return exception;
    }

    public static ExternalServiceException CreateExternalServiceException(
        string serviceName,
        string operation,
        Exception innerException,
        int? statusCode = null)
    {
        var message = $"Error calling {serviceName}.{operation}: {innerException.Message}";
        
        return new ExternalServiceException(
            serviceName,
            operation,
            message,
            statusCode,
            innerException: innerException);
    }

    public static OrderProcessingException CreateOrderProcessingException(
        string orderId,
        OrderProcessingStep failedStep,
        Exception innerException)
    {
        var message = $"Order processing failed at step {failedStep}: {innerException.Message}";
        
        return new OrderProcessingException(
            orderId,
            failedStep,
            message,
            innerException: innerException);
    }
}
  

14. Error Handling in Background Services <a name="background-services"></a>

Robust Background Service with Error Handling

  
    public class OrderProcessingBackgroundService : BackgroundService
{
    private readonly ILogger<OrderProcessingBackgroundService> _logger;
    private readonly IServiceProvider _serviceProvider;
    private readonly IHostApplicationLifetime _hostApplicationLifetime;
    private readonly IConfiguration _configuration;

    public OrderProcessingBackgroundService(
        ILogger<OrderProcessingBackgroundService> logger,
        IServiceProvider serviceProvider,
        IHostApplicationLifetime hostApplicationLifetime,
        IConfiguration configuration)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
        _hostApplicationLifetime = hostApplicationLifetime;
        _configuration = configuration;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Order processing background service started");

        // Wait for application to fully start
        await WaitForApplicationStartup(stoppingToken);

        var retryPolicy = CreateRetryPolicy();
        var circuitBreakerPolicy = CreateCircuitBreakerPolicy();

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await circuitBreakerPolicy.ExecuteAsync(async () =>
                {
                    await retryPolicy.ExecuteAsync(async () =>
                    {
                        await ProcessPendingOrdersAsync(stoppingToken);
                    });
                });

                // Wait before next iteration
                await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
            }
            catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Order processing service stopping due to cancellation");
                break;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unexpected error in order processing service");
                
                // Don't immediately retry on unexpected errors
                await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
            }
        }

        _logger.LogInformation("Order processing background service stopped");
    }

    private async Task ProcessPendingOrdersAsync(CancellationToken cancellationToken)
    {
        using var scope = _serviceProvider.CreateScope();
        var orderProcessor = scope.ServiceProvider.GetRequiredService<IOrderProcessor>();
        var errorMonitoring = scope.ServiceProvider.GetRequiredService<IErrorMonitoringService>();

        try
        {
            _logger.LogInformation("Starting to process pending orders");

            var result = await orderProcessor.ProcessPendingOrdersAsync(cancellationToken);

            _logger.LogInformation(
                "Order processing completed. Processed: {Processed}, Failed: {Failed}, Total: {Total}",
                result.ProcessedCount,
                result.FailedCount,
                result.TotalCount);

            if (result.FailedCount > 0)
            {
                await errorMonitoring.TrackErrorAsync(
                    "ORDER_PROCESSING_FAILURES",
                    $"{result.FailedCount} orders failed processing",
                    new Dictionary<string, object>
                    {
                        ["processedCount"] = result.ProcessedCount,
                        ["failedCount"] = result.FailedCount,
                        ["totalCount"] = result.TotalCount
                    });
            }
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            await errorMonitoring.TrackExceptionAsync(ex, new Dictionary<string, object>
            {
                ["backgroundService"] = nameof(OrderProcessingBackgroundService),
                ["operation"] = "ProcessPendingOrders"
            });

            throw; // Re-throw to let policies handle it
        }
    }

    private async Task WaitForApplicationStartup(CancellationToken stoppingToken)
    {
        var startedSource = new TaskCompletionSource();
        var cancelledSource = new TaskCompletionSource();

        await using var registration1 = _hostApplicationLifetime.ApplicationStarted.Register(
            () => startedSource.SetResult());
        await using var registration2 = stoppingToken.Register(
            () => cancelledSource.SetResult());

        await Task.WhenAny(startedSource.Task, cancelledSource.Task);

        if (stoppingToken.IsCancellationRequested)
        {
            throw new OperationCanceledException("Application startup was cancelled", stoppingToken);
        }

        _logger.LogInformation("Application startup completed, beginning order processing");
    }

    private IAsyncPolicy CreateRetryPolicy()
    {
        return Policy
            .Handle<Exception>(ex => !IsCriticalException(ex))
            .WaitAndRetryAsync(
                retryCount: 3,
                sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (exception, delay, retryCount, context) =>
                {
                    _logger.LogWarning(
                        "Retry {RetryCount} after {Delay}ms for order processing. Error: {Error}",
                        retryCount,
                        delay.TotalMilliseconds,
                        exception.Message);
                });
    }

    private IAsyncPolicy CreateCircuitBreakerPolicy()
    {
        return Policy
            .Handle<Exception>()
            .CircuitBreakerAsync(
                exceptionsAllowedBeforeBreaking: 5,
                durationOfBreak: TimeSpan.FromMinutes(5),
                onBreak: (exception, timespan) =>
                {
                    _logger.LogError(
                        "Circuit breaker opened for {Timespan} due to repeated failures: {Error}",
                        timespan,
                        exception.Message);
                },
                onReset: () =>
                {
                    _logger.LogInformation("Circuit breaker reset for order processing");
                });
    }

    private static bool IsCriticalException(Exception ex)
    {
        return ex is OutOfMemoryException ||
               ex is StackOverflowException ||
               ex is AccessViolationException;
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Order processing background service is stopping");

        // Give the service 30 seconds to complete current operations
        using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        cts.CancelAfter(TimeSpan.FromSeconds(30));

        await base.StopAsync(cts.Token);

        _logger.LogInformation("Order processing background service stopped gracefully");
    }
}
  

Periodic Background Task with Error Recovery

  
    public class PeriodicHealthCheckService : BackgroundService
{
    private readonly ILogger<PeriodicHealthCheckService> _logger;
    private readonly IServiceProvider _serviceProvider;
    private readonly PeriodicTimer _timer;
    private readonly TimeSpan _healthCheckInterval;

    public PeriodicHealthCheckService(
        ILogger<PeriodicHealthCheckService> logger,
        IServiceProvider serviceProvider,
        IConfiguration configuration)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
        _healthCheckInterval = TimeSpan.FromMinutes(5);
        _timer = new PeriodicTimer(_healthCheckInterval);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Periodic health check service started");

        try
        {
            while (await _timer.WaitForNextTickAsync(stoppingToken))
            {
                await PerformHealthCheckAsync(stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("Periodic health check service stopping");
        }
        finally
        {
            _timer.Dispose();
        }

        _logger.LogInformation("Periodic health check service stopped");
    }

    private async Task PerformHealthCheckAsync(CancellationToken cancellationToken)
    {
        using var scope = _serviceProvider.CreateScope();
        var healthCheckService = scope.ServiceProvider.GetRequiredService<IHealthCheckService>();
        
        var stopwatch = Stopwatch.StartNew();

        try
        {
            _logger.LogDebug("Starting periodic health check");

            var healthReport = await healthCheckService.CheckHealthAsync(cancellationToken);

            stopwatch.Stop();

            if (healthReport.Status == HealthStatus.Healthy)
            {
                _logger.LogInformation(
                    "Health check completed successfully. Status: {Status}, Duration: {ElapsedMs}ms",
                    healthReport.Status,
                    stopwatch.ElapsedMilliseconds);
            }
            else
            {
                _logger.LogWarning(
                    "Health check completed with issues. Status: {Status}, Duration: {ElapsedMs}ms",
                    healthReport.Status,
                    stopwatch.ElapsedMilliseconds);

                // Log unhealthy entries
                foreach (var entry in healthReport.Entries.Where(e => e.Value.Status != HealthStatus.Healthy))
                {
                    _logger.LogWarning(
                        "Unhealthy component: {Component}, Status: {Status}, Description: {Description}",
                        entry.Key,
                        entry.Value.Status,
                        entry.Value.Description);
                }
            }

            // Track health metrics
            await TrackHealthMetricsAsync(healthReport, stopwatch.Elapsed);
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            _logger.LogError(ex, "Health check failed after {ElapsedMs}ms", stopwatch.ElapsedMilliseconds);
            
            // Don't throw - we want the service to continue running
            await TrackHealthCheckFailureAsync(ex);
        }
    }

    private async Task TrackHealthMetricsAsync(HealthReport healthReport, TimeSpan duration)
    {
        try
        {
            using var scope = _serviceProvider.CreateScope();
            var metricsService = scope.ServiceProvider.GetRequiredService<IMetricsService>();

            await metricsService.GaugeAsync(
                "health.check.duration",
                duration.TotalMilliseconds,
                new Dictionary<string, object>
                {
                    ["status"] = healthReport.Status.ToString()
                });

            await metricsService.GaugeAsync(
                "health.check.status",
                healthReport.Status == HealthStatus.Healthy ? 1 : 0);

            // Track individual component health
            foreach (var entry in healthReport.Entries)
            {
                await metricsService.GaugeAsync(
                    "health.component.status",
                    entry.Value.Status == HealthStatus.Healthy ? 1 : 0,
                    new Dictionary<string, object>
                    {
                        ["component"] = entry.Key
                    });
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to track health metrics");
        }
    }

    private async Task TrackHealthCheckFailureAsync(Exception exception)
    {
        try
        {
            using var scope = _serviceProvider.CreateScope();
            var errorMonitoring = scope.ServiceProvider.GetRequiredService<IErrorMonitoringService>();

            await errorMonitoring.TrackErrorAsync(
                "HEALTH_CHECK_FAILED",
                "Periodic health check failed",
                new Dictionary<string, object>
                {
                    ["exceptionType"] = exception.GetType().Name,
                    ["exceptionMessage"] = exception.Message
                });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to track health check failure");
        }
    }
}
  

15. Health Checks & Diagnostics <a name="health-checks"></a>

Comprehensive Health Check Setup

  
    public static class HealthCheckConfig
{
    public static IServiceCollection AddCustomHealthChecks(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddHealthChecks()
            // Basic system health checks
            .AddCheck<MemoryHealthCheck>("memory", tags: new[] { "system" })
            .AddCheck<DiskStorageHealthCheck>("disk", tags: new[] { "system" })
            
            // Database health checks
            .AddSqlServer(
                connectionString: configuration.GetConnectionString("DefaultConnection"),
                name: "sqlserver",
                failureStatus: HealthStatus.Degraded,
                tags: new[] { "database", "sqlserver" })
            .AddRedis(
                redisConnectionString: configuration.GetConnectionString("Redis"),
                name: "redis",
                failureStatus: HealthStatus.Degraded,
                tags: new[] { "cache", "redis" })
            
            // External service health checks
            .AddUrlGroup(
                uri: new Uri(configuration["ExternalServices:PaymentGateway"] + "/health"),
                name: "payment-gateway",
                failureStatus: HealthStatus.Degraded,
                tags: new[] { "external", "payment" })
            .AddUrlGroup(
                uri: new Uri(configuration["ExternalServices:ShippingService"] + "/health"),
                name: "shipping-service",
                failureStatus: HealthStatus.Degraded,
                tags: new[] { "external", "shipping" })
            
            // Custom application health checks
            .AddCheck<OrderQueueHealthCheck>("order-queue", tags: new[] { "application", "queue" })
            .AddCheck<DatabaseConnectionHealthCheck>("database-connections", tags: new[] { "application", "database" })
            .AddCheck<ConfigurationHealthCheck>("configuration", tags: new[] { "application", "config" });

        // Health check UI (for development)
        if (configuration.GetValue<bool>("HealthChecks:EnableUI"))
        {
            services.AddHealthChecksUI()
                .AddInMemoryStorage();
        }

        return services;
    }
}

// Custom health checks
public class MemoryHealthCheck : IHealthCheck
{
    private readonly long _thresholdBytes;

    public MemoryHealthCheck(IConfiguration configuration)
    {
        _thresholdBytes = configuration.GetValue<long>("HealthChecks:MemoryThresholdBytes") ?? 500 * 1024 * 1024; // 500MB
    }

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        var process = Process.GetCurrentProcess();
        var memoryUsed = process.WorkingSet64;

        var data = new Dictionary<string, object>
        {
            ["processMemoryBytes"] = memoryUsed,
            ["thresholdBytes"] = _thresholdBytes,
            ["processName"] = process.ProcessName,
            ["machineName"] = Environment.MachineName
        };

        if (memoryUsed > _thresholdBytes)
        {
            return Task.FromResult(HealthCheckResult.Degraded(
                description: $"Memory usage is high: {FormatBytes(memoryUsed)}",
                exception: null,
                data: data));
        }

        return Task.FromResult(HealthCheckResult.Healthy(
            description: $"Memory usage is normal: {FormatBytes(memoryUsed)}",
            data: data));
    }

    private static string FormatBytes(long bytes)
    {
        string[] suffixes = { "B", "KB", "MB", "GB", "TB" };
        int counter = 0;
        decimal number = bytes;
        
        while (Math.Round(number / 1024) >= 1)
        {
            number /= 1024;
            counter++;
        }
        
        return $"{number:n1} {suffixes[counter]}";
    }
}

public class OrderQueueHealthCheck : IHealthCheck
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<OrderQueueHealthCheck> _logger;

    public OrderQueueHealthCheck(IServiceProvider serviceProvider, ILogger<OrderQueueHealthCheck> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var scope = _serviceProvider.CreateScope();
            var queueService = scope.ServiceProvider.GetRequiredService<IOrderQueueService>();

            var queueStatus = await queueService.GetQueueStatusAsync(cancellationToken);

            var data = new Dictionary<string, object>
            {
                ["pendingOrders"] = queueStatus.PendingCount,
                ["processingOrders"] = queueStatus.ProcessingCount,
                ["failedOrders"] = queueStatus.FailedCount,
                ["lastProcessed"] = queueStatus.LastProcessedAt,
                ["queueName"] = queueStatus.QueueName
            };

            if (queueStatus.PendingCount > 1000)
            {
                return HealthCheckResult.Degraded(
                    description: $"Order queue is backing up: {queueStatus.PendingCount} pending orders",
                    data: data);
            }

            if (queueStatus.FailedCount > 100)
            {
                return HealthCheckResult.Unhealthy(
                    description: $"High number of failed orders: {queueStatus.FailedCount}",
                    data: data);
            }

            return HealthCheckResult.Healthy(
                description: $"Order queue is healthy: {queueStatus.PendingCount} pending, {queueStatus.FailedCount} failed",
                data: data);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking order queue health");
            return HealthCheckResult.Unhealthy(
                description: "Failed to check order queue health",
                exception: ex);
        }
    }
}

public class DatabaseConnectionHealthCheck : IHealthCheck
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<DatabaseConnectionHealthCheck> _logger;

    public DatabaseConnectionHealthCheck(IServiceProvider serviceProvider, ILogger<DatabaseConnectionHealthCheck> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var scope = _serviceProvider.CreateScope();
            var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

            // Test database connection
            var canConnect = await context.Database.CanConnectAsync(cancellationToken);
            
            if (!canConnect)
            {
                return HealthCheckResult.Unhealthy("Cannot connect to database");
            }

            // Test basic query
            var activeConnections = await context.Database
                .SqlQueryRaw<int>("SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE status = 'running'")
                .FirstOrDefaultAsync(cancellationToken);

            var data = new Dictionary<string, object>
            {
                ["activeConnections"] = activeConnections,
                ["database"] = context.Database.GetDbConnection().Database,
                ["dataSource"] = context.Database.GetDbConnection().DataSource
            };

            if (activeConnections > 100)
            {
                return HealthCheckResult.Degraded(
                    description: $"High number of database connections: {activeConnections}",
                    data: data);
            }

            return HealthCheckResult.Healthy(
                description: $"Database connections are healthy: {activeConnections} active",
                data: data);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking database connection health");
            return HealthCheckResult.Unhealthy(
                description: "Failed to check database connection health",
                exception: ex);
        }
    }
}
  

Health Check Endpoints Configuration

  
    public static class EndpointRouteBuilderExtensions
{
    public static IEndpointRouteBuilder MapCustomHealthChecks(this IEndpointRouteBuilder endpoints)
    {
        // Basic health check
        endpoints.MapHealthChecks("/health", new HealthCheckOptions
        {
            ResponseWriter = WriteResponse,
            Predicate = check => check.Tags.Contains("system") || check.Tags.Contains("database")
        });

        // Ready check (for load balancers)
        endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions
        {
            ResponseWriter = WriteResponse,
            Predicate = check => check.Tags.Contains("database") || check.Tags.Contains("external")
        });

        // Live check (for liveness probes)
        endpoints.MapHealthChecks("/health/live", new HealthCheckOptions
        {
            ResponseWriter = WriteResponse,
            Predicate = _ => false // No checks for liveness
        });

        // Detailed health check
        endpoints.MapHealthChecks("/health/detailed", new HealthCheckOptions
        {
            ResponseWriter = WriteDetailedResponse,
            Predicate = _ => true // All checks
        });

        // Health check UI
        endpoints.MapHealthChecksUI(options =>
        {
            options.UIPath = "/health-ui";
            options.ApiPath = "/health-ui-api";
        });

        return endpoints;
    }

    private static async Task WriteResponse(HttpContext context, HealthReport report)
    {
        var result = new
        {
            status = report.Status.ToString(),
            totalDuration = report.TotalDuration.TotalMilliseconds,
            checks = report.Entries.Select(entry => new
            {
                name = entry.Key,
                status = entry.Value.Status.ToString(),
                duration = entry.Value.Duration.TotalMilliseconds,
                description = entry.Value.Description
            })
        };

        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonSerializer.Serialize(result, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = false
        }));
    }

    private static async Task WriteDetailedResponse(HttpContext context, HealthReport report)
    {
        var result = new
        {
            status = report.Status.ToString(),
            totalDuration = report.TotalDuration.TotalMilliseconds,
            timestamp = DateTime.UtcNow,
            machineName = Environment.MachineName,
            checks = report.Entries.Select(entry => new
            {
                name = entry.Key,
                status = entry.Value.Status.ToString(),
                duration = entry.Value.Duration.TotalMilliseconds,
                description = entry.Value.Description,
                exception = entry.Value.Exception?.Message,
                data = entry.Value.Data
            })
        };

        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(JsonSerializer.Serialize(result, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = context.Request.Query.ContainsKey("pretty")
        }));
    }
}
  

16. Performance Considerations <a name="performance"></a>

High-Performance Logging

  
    public static class HighPerformanceLogger
{
    // Use source generation for high-performance logging
    [LoggerMessage(
        EventId = 1000,
        Level = LogLevel.Error,
        Message = "Order processing failed for order {OrderId}. Customer: {CustomerId}, Error: {ErrorMessage}")]
    public static partial void LogOrderProcessingError(
        this ILogger logger, string orderId, string customerId, string errorMessage);

    [LoggerMessage(
        EventId = 1001,
        Level = LogLevel.Warning,
        Message = "Validation failed for order {OrderId}. Errors: {ErrorCount}")]
    public static partial void LogOrderValidationWarning(
        this ILogger logger, string orderId, int errorCount);

    [LoggerMessage(
        EventId = 1002,
        Level = LogLevel.Information,
        Message = "Order {OrderId} processed successfully in {ProcessingTimeMs}ms")]
    public static partial void LogOrderProcessed(
        this ILogger logger, string orderId, long processingTimeMs);

    // Conditional logging methods
    public static void LogDebugIfEnabled(this ILogger logger, string message, params object[] args)
    {
        if (logger.IsEnabled(LogLevel.Debug))
        {
            logger.LogDebug(message, args);
        }
    }

    public static void LogTraceIfEnabled(this ILogger logger, Func<string> messageFactory)
    {
        if (logger.IsEnabled(LogLevel.Trace))
        {
            logger.LogTrace(messageFactory());
        }
    }
}

// High-performance exception tracking
public class PerformanceOptimizedExceptionHandler : IExceptionHandler
{
    private readonly ILogger _logger;
    private readonly ConcurrentDictionary<Type, bool> _shouldLogCache = new();

    public PerformanceOptimizedExceptionHandler(ILogger<PerformanceOptimizedExceptionHandler> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        // Fast path: check if we should log this exception
        if (!ShouldLogException(exception))
        {
            return false;
        }

        // Use structured logging with cached log level
        var logLevel = GetLogLevel(exception);
        if (_logger.IsEnabled(logLevel))
        {
            _logger.Log(
                logLevel,
                exception,
                "Exception handled: {ExceptionType} for request {RequestPath}",
                exception.GetType().Name,
                httpContext.Request.Path);
        }

        await WriteProblemDetailsAsync(httpContext, exception, cancellationToken);
        return true;
    }

    private bool ShouldLogException(Exception exception)
    {
        var exceptionType = exception.GetType();
        
        return _shouldLogCache.GetOrAdd(exceptionType, type =>
        {
            return type != typeof(ValidationException) &&
                   type != typeof(NotFoundException) &&
                   type != typeof(UnauthorizedAccessException);
        });
    }

    private static LogLevel GetLogLevel(Exception exception) => exception switch
    {
        ValidationException => LogLevel.Warning,
        NotFoundException => LogLevel.Warning,
        UnauthorizedAccessException => LogLevel.Warning,
        BusinessRuleException => LogLevel.Information,
        _ => LogLevel.Error
    };

    private static async Task WriteProblemDetailsAsync(
        HttpContext httpContext,
        Exception exception,
        CancellationToken cancellationToken)
    {
        var problemDetails = CreateProblemDetails(exception);
        
        httpContext.Response.StatusCode = problemDetails.Status ?? 500;
        httpContext.Response.ContentType = "application/problem+json";

        await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken);
    }

    private static ProblemDetails CreateProblemDetails(Exception exception)
    {
        return exception switch
        {
            ValidationException validationEx => new ValidationProblemDetails(validationEx.Errors),
            NotFoundException notFoundEx => new ProblemDetails
            {
                Status = 404,
                Title = "Not Found",
                Detail = notFoundEx.Message
            },
            UnauthorizedAccessException => new ProblemDetails
            {
                Status = 401,
                Title = "Unauthorized",
                Detail = "Authentication is required"
            },
            _ => new ProblemDetails
            {
                Status = 500,
                Title = "Internal Server Error",
                Detail = "An unexpected error occurred"
            }
        };
    }
}
  

Memory-Optimized Error Tracking

  
    public class MemoryOptimizedErrorTracker
{
    private readonly ILogger _logger;
    private readonly ConcurrentQueue<ErrorEntry> _recentErrors;
    private readonly int _maxTrackedErrors;
    private readonly TimeSpan _errorRetentionPeriod;

    public MemoryOptimizedErrorTracker(
        ILogger<MemoryOptimizedErrorTracker> logger,
        IConfiguration configuration)
    {
        _logger = logger;
        _maxTrackedErrors = configuration.GetValue<int>("ErrorTracking:MaxTrackedErrors") ?? 1000;
        _errorRetentionPeriod = TimeSpan.FromMinutes(
            configuration.GetValue<int>("ErrorTracking:RetentionMinutes") ?? 60);
        
        _recentErrors = new ConcurrentQueue<ErrorEntry>();
    }

    public void TrackError(string errorCode, string message, Exception exception = null)
    {
        var errorEntry = new ErrorEntry
        {
            ErrorCode = errorCode,
            Message = message,
            ExceptionType = exception?.GetType().FullName,
            Timestamp = DateTime.UtcNow,
            MachineName = Environment.MachineName
        };

        _recentErrors.Enqueue(errorEntry);

        // Clean up old entries
        CleanupOldEntries();

        // Log the error
        if (exception != null)
        {
            _logger.LogError(exception, "Error tracked: {ErrorCode} - {Message}", errorCode, message);
        }
        else
        {
            _logger.LogError("Error tracked: {ErrorCode} - {Message}", errorCode, message);
        }
    }

    public IReadOnlyList<ErrorEntry> GetRecentErrors(TimeSpan? timeWindow = null)
    {
        var window = timeWindow ?? _errorRetentionPeriod;
        var cutoff = DateTime.UtcNow - window;

        return _recentErrors
            .Where(e => e.Timestamp >= cutoff)
            .OrderByDescending(e => e.Timestamp)
            .ToList()
            .AsReadOnly();
    }

    public ErrorStatistics GetErrorStatistics(TimeSpan timeWindow)
    {
        var errors = GetRecentErrors(timeWindow);
        
        return new ErrorStatistics
        {
            TotalErrors = errors.Count,
            ErrorCounts = errors.GroupBy(e => e.ErrorCode)
                .ToDictionary(g => g.Key, g => g.Count()),
            TimeWindow = timeWindow
        };
    }

    private void CleanupOldEntries()
    {
        var cutoff = DateTime.UtcNow - _errorRetentionPeriod;

        while (_recentErrors.Count > _maxTrackedErrors || 
               (_recentErrors.TryPeek(out var oldest) && oldest.Timestamp < cutoff))
        {
            _recentErrors.TryDequeue(out _);
        }
    }
}

public class ErrorEntry
{
    public string ErrorCode { get; set; }
    public string Message { get; set; }
    public string ExceptionType { get; set; }
    public DateTime Timestamp { get; set; }
    public string MachineName { get; set; }
}

public class ErrorStatistics
{
    public int TotalErrors { get; set; }
    public Dictionary<string, int> ErrorCounts { get; set; } = new();
    public TimeSpan TimeWindow { get; set; }
}
  

17. Security Aspects of Error Handling <a name="security"></a>

Secure Error Information Exposure

  
    public class SecureExceptionHandler
{
    private readonly IWebHostEnvironment _environment;
    private readonly ILogger<SecureExceptionHandler> _logger;

    public SecureExceptionHandler(IWebHostEnvironment environment, ILogger<SecureExceptionHandler> logger)
    {
        _environment = environment;
        _logger = logger;
    }

    public async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        // Log the full exception securely
        LogExceptionSecurely(exception, context);

        // Create a safe response based on environment
        var response = CreateSecureResponse(exception, context);

        await WriteSecureResponseAsync(context, response);
    }

    private void LogExceptionSecurely(Exception exception, HttpContext context)
    {
        // Sanitize sensitive data before logging
        var sanitizedException = SanitizeException(exception);
        
        _logger.LogError(
            sanitizedException,
            "Secure exception handling for request: {Method} {Path}",
            context.Request.Method,
            context.Request.Path);
    }

    private Exception SanitizeException(Exception exception)
    {
        // Create a sanitized version of the exception
        // that doesn't include sensitive information
        var sanitizedMessage = SanitizeErrorMessage(exception.Message);
        
        var sanitizedException = new Exception(sanitizedMessage)
        {
            // Preserve stack trace for debugging, but sanitize file paths
            StackTrace = SanitizeStackTrace(exception.StackTrace)
        };

        return sanitizedException;
    }

    private string SanitizeErrorMessage(string errorMessage)
    {
        // Remove sensitive information from error messages
        var sanitized = errorMessage;
        
        // Remove connection strings
        sanitized = Regex.Replace(sanitized, 
            @"(Password|Pwd|ConnectionString)=[^;]+", 
            "$1=***", 
            RegexOptions.IgnoreCase);
        
        // Remove email addresses
        sanitized = Regex.Replace(sanitized,
            @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
            "***@***.***");
        
        // Remove credit card numbers
        sanitized = Regex.Replace(sanitized,
            @"\b(?:\d[ -]*?){13,16}\b",
            "****************");
        
        return sanitized;
    }

    private string SanitizeStackTrace(string stackTrace)
    {
        if (string.IsNullOrEmpty(stackTrace))
            return stackTrace;

        // Remove full file paths from stack trace
        return Regex.Replace(stackTrace,
            @"( in )(.+)(:line \d+)",
            "$1***$3");
    }

    private object CreateSecureResponse(Exception exception, HttpContext context)
    {
        if (_environment.IsDevelopment())
        {
            // In development, provide more details
            return new
            {
                type = exception.GetType().Name,
                message = exception.Message,
                stackTrace = exception.StackTrace,
                innerException = exception.InnerException?.Message
            };
        }
        else
        {
            // In production, provide minimal information
            var errorId = Guid.NewGuid().ToString();
            
            // Log the correlation between error ID and actual exception
            _logger.LogInformation("Error ID {ErrorId} corresponds to exception: {ExceptionType}", 
                errorId, exception.GetType().Name);

            return new
            {
                errorId = errorId,
                message = "An error occurred. Please contact support with the error ID.",
                supportReference = errorId
            };
        }
    }

    private async Task WriteSecureResponseAsync(HttpContext context, object response)
    {
        context.Response.StatusCode = 500;
        context.Response.ContentType = "application/json";

        await context.Response.WriteAsJsonAsync(response);
    }
}

// Security-focused exception types
public class SecurityException : Exception
{
    public SecurityEventType EventType { get; }
    public string UserId { get; }
    public string IpAddress { get; }
    public string Resource { get; }

    public SecurityException(
        SecurityEventType eventType,
        string message,
        string userId = null,
        string ipAddress = null,
        string resource = null,
        Exception innerException = null)
        : base(message, innerException)
    {
        EventType = eventType;
        UserId = userId;
        IpAddress = ipAddress;
        Resource = resource;
    }
}

public enum SecurityEventType
{
    AuthenticationFailure,
    AuthorizationFailure,
    InputValidation,
    RateLimiting,
    SuspiciousActivity
}

// Security event logger
public class SecurityEventLogger
{
    private readonly ILogger<SecurityEventLogger> _logger;

    public SecurityEventLogger(ILogger<SecurityEventLogger> logger)
    {
        _logger = logger;
    }

    public void LogSecurityEvent(SecurityEventType eventType, string message, HttpContext context = null)
    {
        var logData = new Dictionary<string, object>
        {
            ["securityEventType"] = eventType.ToString(),
            ["message"] = message,
            ["timestamp"] = DateTime.UtcNow
        };

        if (context != null)
        {
            logData["ipAddress"] = context.Connection.RemoteIpAddress?.ToString();
            logData["userAgent"] = context.Request.Headers["User-Agent"].ToString();
            logData["requestPath"] = context.Request.Path;
            logData["requestMethod"] = context.Request.Method;

            if (context.User.Identity.IsAuthenticated)
            {
                logData["userId"] = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
                logData["userName"] = context.User.Identity.Name;
            }
        }

        _logger.LogWarning(
            "Security event: {EventType}. Message: {Message}. Data: {@LogData}",
            eventType,
            message,
            logData);
    }

    public void LogAuthenticationFailure(string userId, string reason, HttpContext context = null)
    {
        LogSecurityEvent(SecurityEventType.AuthenticationFailure, 
            $"Authentication failed for user {userId}: {reason}", context);
    }

    public void LogAuthorizationFailure(string userId, string resource, string action, HttpContext context = null)
    {
        LogSecurityEvent(SecurityEventType.AuthorizationFailure,
            $"Authorization failed for user {userId} on resource {resource} for action {action}", context);
    }

    public void LogSuspiciousActivity(string activity, HttpContext context = null)
    {
        LogSecurityEvent(SecurityEventType.SuspiciousActivity,
            $"Suspicious activity detected: {activity}", context);
    }
}
  

18. Testing Error Scenarios <a name="testing"></a>

Unit Tests for Error Handling

  
    public class ErrorHandlingTests
{
    [Test]
    public async Task GlobalExceptionMiddleware_HandlesException_Correctly()
    {
        // Arrange
        var loggerMock = new Mock<ILogger<GlobalExceptionMiddleware>>();
        var environmentMock = new Mock<IWebHostEnvironment>();
        environmentMock.Setup(x => x.IsDevelopment()).Returns(false);

        var middleware = new GlobalExceptionMiddleware(
            next: async (context) => throw new InvalidOperationException("Test exception"),
            logger: loggerMock.Object,
            environment: environmentMock.Object);

        var httpContext = new DefaultHttpContext();
        httpContext.Response.Body = new MemoryStream();

        // Act
        await middleware.InvokeAsync(httpContext);

        // Assert
        httpContext.Response.StatusCode.Should().Be(500);
        httpContext.Response.ContentType.Should().Be("application/problem+json");

        httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
        var responseBody = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
        var problemDetails = JsonSerializer.Deserialize<ProblemDetails>(responseBody);

        problemDetails.Should().NotBeNull();
        problemDetails.Title.Should().Be("Internal Server Error");
        problemDetails.Status.Should().Be(500);

        // Verify logging occurred
        loggerMock.Verify(
            x => x.Log(
                LogLevel.Error,
                It.IsAny<EventId>(),
                It.Is<It.IsAnyType>((v, t) => true),
                It.IsAny<Exception>(),
                It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)),
            Times.Once);
    }

    [Test]
    public void ValidationException_CreatesCorrectProblemDetails()
    {
        // Arrange
        var errors = new Dictionary<string, string[]>
        {
            ["Email"] = new[] { "Email is required", "Email must be valid" },
            ["Password"] = new[] { "Password must be at least 8 characters" }
        };

        var exception = new ValidationException(errors);

        // Act
        var problemDetails = new ValidationProblemDetails(exception.Errors);

        // Assert
        problemDetails.Should().NotBeNull();
        problemDetails.Title.Should().Be("Validation Error");
        problemDetails.Status.Should().Be(400);
        problemDetails.Errors.Should().BeEquivalentTo(errors);
    }

    [Test]
    public async Task OrderProcessor_HandlesPaymentFailure_Gracefully()
    {
        // Arrange
        var loggerMock = new Mock<ILogger<OrderProcessor>>();
        var paymentServiceMock = new Mock<IPaymentService>();
        
        paymentServiceMock
            .Setup(x => x.ProcessPaymentAsync(It.IsAny<PaymentRequest>()))
            .ThrowsAsync(new PaymentException("Insufficient funds", isRetryable: false));

        var orderProcessor = new OrderProcessor(loggerMock.Object, paymentServiceMock.Object);

        var orderRequest = new OrderRequest
        {
            OrderId = "TEST123",
            CustomerId = "CUST456",
            TotalAmount = 100.00m
        };

        // Act & Assert
        await orderProcessor
            .Invoking(x => x.ProcessOrderAsync(orderRequest))
            .Should()
            .ThrowAsync<PaymentException>()
            .WithMessage("Insufficient funds");

        paymentServiceMock.Verify(
            x => x.ProcessPaymentAsync(It.IsAny<PaymentRequest>()),
            Times.Once);

        loggerMock.Verify(
            x => x.Log(
                LogLevel.Error,
                It.IsAny<EventId>(),
                It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("Payment processing failed")),
                It.IsAny<Exception>(),
                It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)),
            Times.Once);
    }

    [Test]
    public async Task ResilientDatabaseExecutor_RetriesOnTransientErrors()
    {
        // Arrange
        var loggerMock = new Mock<ILogger<ResilientDatabaseExecutor>>();
        var executor = new ResilientDatabaseExecutor(loggerMock.Object);

        var callCount = 0;
        var operation = async () =>
        {
            callCount++;
            if (callCount < 3)
            {
                throw new SqlException("Timeout expired",  -2); // Transient error
            }
            return await Task.FromResult("Success");
        };

        // Act
        var result = await executor.ExecuteAsync(operation, "TestOperation");

        // Assert
        result.Should().Be("Success");
        callCount.Should().Be(3); // Should have retried twice

        loggerMock.Verify(
            x => x.Log(
                LogLevel.Warning,
                It.IsAny<EventId>(),
                It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("Retry")),
                It.IsAny<Exception>(),
                It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)),
            Times.Exactly(2)); // Two retries
    }
}

// Integration tests for error scenarios
public class ErrorHandlingIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public ErrorHandlingIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                // Mock external services that might fail
                services.AddScoped<IPaymentService, MockPaymentService>();
            });
        });
    }

    [Test]
    public async Task ApiEndpoint_ReturnsValidationProblem_OnInvalidInput()
    {
        // Arrange
        var client = _factory.CreateClient();
        var invalidRequest = new { }; // Missing required fields

        // Act
        var response = await client.PostAsJsonAsync("/api/orders", invalidRequest);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
        
        var content = await response.Content.ReadAsStringAsync();
        var problemDetails = JsonSerializer.Deserialize<ValidationProblemDetails>(content);

        problemDetails.Should().NotBeNull();
        problemDetails.Type.Should().Contain("validation");
        problemDetails.Errors.Should().NotBeEmpty();
    }

    [Test]
    public async Task ApiEndpoint_ReturnsNotFound_ForMissingResource()
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/orders/999999");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
        
        var content = await response.Content.ReadAsStringAsync();
        var problemDetails = JsonSerializer.Deserialize<ProblemDetails>(content);

        problemDetails.Should().NotBeNull();
        problemDetails.Title.Should().Be("Resource Not Found");
    }

    [Test]
    public async Task HealthCheckEndpoint_ReturnsServiceUnhealthy_WhenDatabaseDown()
    {
        // Arrange
        var factory = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                // Replace with unhealthy database service
                services.AddScoped<IDatabaseHealthCheck, UnhealthyDatabaseHealthCheck>();
            });
        });

        var client = factory.CreateClient();

        // Act
        var response = await client.GetAsync("/health");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.ServiceUnavailable);
    }
}

// Mock services for testing
public class MockPaymentService : IPaymentService
{
    public Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
    {
        throw new PaymentException("Mock payment failure", isRetryable: false);
    }
}

public class UnhealthyDatabaseHealthCheck : IDatabaseHealthCheck
{
    public Task<bool> IsHealthyAsync()
    {
        return Task.FromResult(false);
    }
}
  

19. Real-World Implementation Example

Complete E-commerce Error Handling System

  
    // Complete error handling setup for e-commerce
public static class ErrorHandlingSetup
{
    public static IServiceCollection AddEcommerceErrorHandling(this IServiceCollection services, IConfiguration configuration)
    {
        // Add structured logging
        services.AddSerilogLogging(configuration);
        
        // Add error monitoring
        services.AddErrorMonitoring(configuration);
        
        // Add health checks
        services.AddCustomHealthChecks(configuration);
        
        // Add exception handlers
        services.AddExceptionHandler<GlobalExceptionHandler>();
        services.AddExceptionHandler<ValidationExceptionHandler>();
        services.AddExceptionHandler<BusinessExceptionHandler>();
        
        // Add problem details service
        services.AddProblemDetails();
        
        // Add resilience policies
        services.AddResiliencePolicies(configuration);
        
        return services;
    }

    public static IApplicationBuilder UseEcommerceErrorHandling(this IApplicationBuilder app)
    {
        var environment = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();

        if (environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/error");
            app.UseHsts();
        }

        app.UseStatusCodePagesWithReExecute("/error/{0}");
        
        // Custom middleware
        app.UseMiddleware<CorrelationIdMiddleware>();
        app.UseMiddleware<GlobalExceptionMiddleware>();
        app.UseMiddleware<RequestLoggingMiddleware>();
        
        // Health checks
        app.UseHealthChecks("/health");
        app.UseCustomHealthChecks();

        return app;
    }
}

// Complete order service with comprehensive error handling
public class OrderService : IOrderService
{
    private readonly ILogger<OrderService> _logger;
    private readonly IOrderRepository _orderRepository;
    private readonly IPaymentService _paymentService;
    private readonly IInventoryService _inventoryService;
    private readonly IEmailService _emailService;
    private readonly IResiliencePolicyProvider _policyProvider;
    private readonly IErrorMonitoringService _errorMonitoring;

    public OrderService(
        ILogger<OrderService> logger,
        IOrderRepository orderRepository,
        IPaymentService paymentService,
        IInventoryService inventoryService,
        IEmailService emailService,
        IResiliencePolicyProvider policyProvider,
        IErrorMonitoringService errorMonitoring)
    {
        _logger = logger;
        _orderRepository = orderRepository;
        _paymentService = paymentService;
        _inventoryService = inventoryService;
        _emailService = emailService;
        _policyProvider = policyProvider;
        _errorMonitoring = errorMonitoring;
    }

    public async Task<OrderResult> PlaceOrderAsync(OrderRequest request)
    {
        using var activity = _logger.BeginScope(new Dictionary<string, object>
        {
            ["OrderId"] = request.OrderId,
            ["CustomerId"] = request.CustomerId,
            ["Operation"] = "PlaceOrder"
        });

        try
        {
            _logger.LogInformation("Starting order placement for order {OrderId}", request.OrderId);

            // Validate order
            await ValidateOrderAsync(request);

            // Process payment with retry policy
            var paymentResult = await ProcessPaymentWithResilienceAsync(request);

            // Reserve inventory
            await ReserveInventoryAsync(request.Items);

            // Create order
            var order = await CreateOrderAsync(request, paymentResult);

            // Send confirmation email (fire-and-forget)
            _ = SendOrderConfirmationAsync(order);

            _logger.LogInformation("Order placed successfully. OrderId: {OrderId}, PaymentId: {PaymentId}", 
                order.Id, paymentResult.PaymentId);

            await _errorMonitoring.TrackBusinessEventAsync("OrderPlaced", new
            {
                OrderId = order.Id,
                CustomerId = order.CustomerId,
                TotalAmount = order.TotalAmount,
                PaymentMethod = order.PaymentMethod
            });

            return OrderResult.Success(order);
        }
        catch (ValidationException ex)
        {
            _logger.LogWarning(ex, "Order validation failed for order {OrderId}", request.OrderId);
            await _errorMonitoring.TrackErrorAsync("ORDER_VALIDATION_FAILED", ex.Message, ex);
            return OrderResult.ValidationFailed(ex.Errors);
        }
        catch (PaymentException ex)
        {
            _logger.LogError(ex, "Payment processing failed for order {OrderId}", request.OrderId);
            await _errorMonitoring.TrackErrorAsync("PAYMENT_PROCESSING_FAILED", ex.Message, ex);
            return OrderResult.PaymentFailed(ex.Message);
        }
        catch (InventoryException ex)
        {
            _logger.LogError(ex, "Inventory reservation failed for order {OrderId}", request.OrderId);
            await _errorMonitoring.TrackErrorAsync("INVENTORY_RESERVATION_FAILED", ex.Message, ex);
            
            // Attempt to refund payment if it was processed
            await AttemptPaymentRefundAsync(request.OrderId);
            
            return OrderResult.InventoryFailed(ex.Message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error placing order {OrderId}", request.OrderId);
            await _errorMonitoring.TrackExceptionAsync(ex, new Dictionary<string, object>
            {
                ["OrderId"] = request.OrderId,
                ["CustomerId"] = request.CustomerId,
                ["Operation"] = "PlaceOrder"
            });
            
            // Attempt cleanup
            await AttemptOrderCleanupAsync(request.OrderId);
            
            throw new OrderProcessingException(request.OrderId, OrderProcessingStep.Processing, 
                "Failed to place order", ex);
        }
    }

    private async Task ValidateOrderAsync(OrderRequest request)
    {
        var validationTasks = new[]
        {
            ValidateCustomerAsync(request.CustomerId),
            ValidateProductsAsync(request.Items),
            ValidateShippingAsync(request.ShippingAddress),
            ValidatePaymentMethodAsync(request.PaymentMethod)
        };

        try
        {
            await Task.WhenAll(validationTasks);
        }
        catch (AggregateException ae)
        {
            var validationErrors = ae.Flatten().InnerExceptions
                .OfType<ValidationException>()
                .SelectMany(ex => ex.Errors)
                .ToDictionary(pair => pair.Key, pair => pair.Value);

            if (validationErrors.Any())
            {
                throw new ValidationException(validationErrors);
            }

            throw;
        }
    }

    private async Task<PaymentResult> ProcessPaymentWithResilienceAsync(OrderRequest request)
    {
        var policy = _policyProvider.GetPaymentProcessingPolicy();

        return await policy.ExecuteAsync(async () =>
        {
            try
            {
                return await _paymentService.ProcessPaymentAsync(request);
            }
            catch (PaymentException ex) when (!ex.IsRetryable)
            {
                _logger.LogError(ex, "Non-retryable payment error for order {OrderId}", request.OrderId);
                throw;
            }
        });
    }

    private async Task ReserveInventoryAsync(IEnumerable<OrderItemRequest> items)
    {
        var reserveTasks = items.Select(item => 
            _inventoryService.ReserveAsync(item.ProductId, item.Quantity));

        var results = await Task.WhenAll(reserveTasks);

        var failures = results.Where(r => !r.Success).ToList();
        if (failures.Any())
        {
            var errorMessages = failures.Select(f => f.ErrorMessage);
            throw new InventoryException($"Inventory reservation failed: {string.Join("; ", errorMessages)}");
        }
    }

    private async Task<Order> CreateOrderAsync(OrderRequest request, PaymentResult paymentResult)
    {
        var order = new Order
        {
            Id = request.OrderId,
            CustomerId = request.CustomerId,
            TotalAmount = request.TotalAmount,
            Currency = request.Currency,
            Status = OrderStatus.Confirmed,
            PaymentId = paymentResult.PaymentId,
            CreatedAt = DateTime.UtcNow,
            Items = request.Items.Select(item => new OrderItem
            {
                ProductId = item.ProductId,
                Quantity = item.Quantity,
                UnitPrice = item.UnitPrice
            }).ToList()
        };

        return await _orderRepository.AddAsync(order);
    }

    private async Task SendOrderConfirmationAsync(Order order)
    {
        try
        {
            await _emailService.SendOrderConfirmationAsync(order);
            _logger.LogInformation("Order confirmation email sent for order {OrderId}", order.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send order confirmation email for order {OrderId}", order.Id);
            // Don't throw - this is a non-critical operation
        }
    }

    private async Task AttemptPaymentRefundAsync(string orderId)
    {
        try
        {
            await _paymentService.RefundPaymentAsync(orderId);
            _logger.LogInformation("Payment refund processed for failed order {OrderId}", orderId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to refund payment for failed order {OrderId}", orderId);
            // Log but don't throw - this is cleanup operation
        }
    }

    private async Task AttemptOrderCleanupAsync(string orderId)
    {
        try
        {
            // Mark order as failed in database
            await _orderRepository.MarkAsFailedAsync(orderId);
            
            // Release any reserved inventory
            await _inventoryService.ReleaseReservationsAsync(orderId);
            
            _logger.LogInformation("Cleanup completed for failed order {OrderId}", orderId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to cleanup failed order {OrderId}", orderId);
            // Log but don't throw - this is best-effort cleanup
        }
    }
}

// Comprehensive error monitoring service
public class EcommerceErrorMonitoringService : IErrorMonitoringService
{
    private readonly TelemetryClient _telemetryClient;
    private readonly ILogger<EcommerceErrorMonitoringService> _logger;
    private readonly IEmailService _emailService;
    private readonly IConfiguration _configuration;

    public EcommerceErrorMonitoringService(
        TelemetryClient telemetryClient,
        ILogger<EcommerceErrorMonitoringService> logger,
        IEmailService emailService,
        IConfiguration configuration)
    {
        _telemetryClient = telemetryClient;
        _logger = logger;
        _emailService = emailService;
        _configuration = configuration;
    }

    public async Task TrackExceptionAsync(Exception exception, IDictionary<string, object> context = null)
    {
        try
        {
            var telemetry = new ExceptionTelemetry(exception);
            
            // Add context
            if (context != null)
            {
                foreach (var kvp in context)
                {
                    telemetry.Properties[kvp.Key] = kvp.Value?.ToString();
                }
            }

            // Add business context
            telemetry.Properties["Application"] = "Ecommerce";
            telemetry.Properties["Environment"] = _configuration["ASPNETCORE_ENVIRONMENT"];
            
            _telemetryClient.TrackException(telemetry);
            _telemetryClient.Flush();

            // Check if we should alert
            if (await ShouldAlertAsync(exception))
            {
                await SendAlertAsync(exception, context);
            }

            _logger.LogInformation("Exception tracked: {ExceptionType}", exception.GetType().Name);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to track exception in error monitoring service");
        }
    }

    public async Task TrackErrorAsync(string errorCode, string message, Exception exception = null)
    {
        var telemetry = new TraceTelemetry(message, SeverityLevel.Error);
        telemetry.Properties["ErrorCode"] = errorCode;
        telemetry.Properties["Application"] = "Ecommerce";

        if (exception != null)
        {
            telemetry.Properties["ExceptionType"] = exception.GetType().Name;
        }

        _telemetryClient.TrackTrace(telemetry);
        _telemetryClient.Flush();

        _logger.LogWarning("Error tracked: {ErrorCode} - {Message}", errorCode, message);
    }

    public async Task TrackBusinessEventAsync(string eventType, object data)
    {
        var telemetry = new EventTelemetry(eventType);
        
        foreach (var prop in data.GetType().GetProperties())
        {
            telemetry.Properties[prop.Name] = prop.GetValue(data)?.ToString();
        }

        _telemetryClient.TrackEvent(telemetry);
        
        _logger.LogInformation("Business event tracked: {EventType}", eventType);
    }

    private async Task<bool> ShouldAlertAsync(Exception exception)
    {
        // Don't alert for expected business exceptions
        if (exception is ValidationException || 
            exception is BusinessRuleException ||
            exception is NotFoundException)
        {
            return false;
        }

        // Alert for critical errors
        if (exception is DatabaseConnectionException ||
            exception is ExternalServiceException ||
            exception is OutOfMemoryException)
        {
            return true;
        }

        // Rate limiting: don't alert for the same error repeatedly
        return true;
    }

    private async Task SendAlertAsync(Exception exception, IDictionary<string, object> context)
    {
        try
        {
            var alertSettings = _configuration.GetSection("ErrorAlerting");
            var enabled = alertSettings.GetValue<bool>("Enabled");
            
            if (!enabled) return;

            var alertMessage = CreateAlertMessage(exception, context);
            
            // Send to configured alert channels
            await SendAlertToChannelsAsync(alertMessage);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send error alert");
        }
    }

    private string CreateAlertMessage(Exception exception, IDictionary<string, object> context)
    {
        var sb = new StringBuilder();
        sb.AppendLine("🚨 ECOMMERCE APPLICATION ALERT");
        sb.AppendLine($"Time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
        sb.AppendLine($"Exception: {exception.GetType().Name}");
        sb.AppendLine($"Message: {exception.Message}");
        
        if (context != null && context.Any())
        {
            sb.AppendLine("Context:");
            foreach (var kvp in context)
            {
                sb.AppendLine($"  {kvp.Key}: {kvp.Value}");
            }
        }

        if (exception.StackTrace != null)
        {
            sb.AppendLine($"Stack Trace: {exception.StackTrace}");
        }

        return sb.ToString();
    }

    private async Task SendAlertToChannelsAsync(string alertMessage)
    {
        var alertChannels = _configuration.GetSection("ErrorAlerting:Channels").Get<string[]>();
        
        var sendTasks = alertChannels.Select(channel => SendToChannelAsync(channel, alertMessage));
        await Task.WhenAll(sendTasks);
    }

    private async Task SendToChannelAsync(string channel, string message)
    {
        try
        {
            switch (channel.ToLowerInvariant())
            {
                case "email":
                    await SendEmailAlertAsync(message);
                    break;
                case "slack":
                    await SendSlackAlertAsync(message);
                    break;
                case "teams":
                    await SendTeamsAlertAsync(message);
                    break;
                default:
                    _logger.LogWarning("Unknown alert channel: {Channel}", channel);
                    break;
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send alert to channel: {Channel}", channel);
        }
    }

    private async Task SendEmailAlertAsync(string message)
    {
        var emailRequest = new EmailRequest
        {
            To = _configuration["ErrorAlerting:EmailRecipients"].Split(','),
            Subject = "Ecommerce Application Alert",
            Body = message
        };

        await _emailService.SendAsync(emailRequest);
    }

    private Task SendSlackAlertAsync(string message)
    {
        // Implementation for Slack webhook
        return Task.CompletedTask;
    }

    private Task SendTeamsAlertAsync(string message)
    {
        // Implementation for Microsoft Teams webhook
        return Task.CompletedTask;
    }
}
  

20. Conclusion & Best Practices

Key Error Handling Principles

  1. Fail Fast: Detect and report errors as early as possible

  2. Fail Gracefully: Provide meaningful feedback and maintain system stability

  3. Log Comprehensively: Capture sufficient context for debugging

  4. Monitor Proactively: Track error patterns and trends

  5. Secure Sensitive Data: Prevent information leakage in error messages

Best Practices Summary

  
    // DO: Use structured logging with sufficient context
_logger.LogError(exception, 
    "Order processing failed. OrderId: {OrderId}, Customer: {CustomerId}, Step: {FailedStep}",
    orderId, customerId, failedStep);

// DON'T: Log without context
_logger.LogError(exception, "Order processing failed");

// DO: Use specific exception types
throw new ValidationException(validationErrors);

// DON'T: Throw generic exceptions
throw new Exception("Something went wrong");

// DO: Handle exceptions at appropriate levels
try
{
    await ProcessOrderAsync(request);
}
catch (ValidationException ex)
{
    // Handle validation errors specifically
    return BadRequest(new ValidationProblemDetails(ex.Errors));
}

// DON'T: Catch all exceptions indiscriminately
try
{
    await ProcessOrderAsync(request);
}
catch (Exception ex)
{
    // This might swallow important exceptions
    _logger.LogError(ex, "Error");
}

// DO: Use resilience patterns
var result = await retryPolicy.ExecuteAsync(async () =>
{
    return await externalService.CallAsync();
});

// DON'T: Make calls without resilience
var result = await externalService.CallAsync(); // May fail on transient issues

// DO: Provide user-friendly error messages
return Problem(
    title: "Payment Failed",
    detail: "We were unable to process your payment. Please check your payment details and try again.",
    statusCode: 400);

// DON'T: Expose technical details to users
return Problem(
    title: "SQL Exception",
    detail: "Timeout expired. The timeout period elapsed prior to completion...",
    statusCode: 500);
  

Continuous Improvement

Error handling is not a one-time setup but an ongoing process:

  1. Regularly Review Logs: Analyze error patterns and trends

  2. Update Error Handling: Adapt to new requirements and technologies

  3. Monitor Performance: Ensure error handling doesn't impact application performance

  4. Conduct Post-Mortems: Learn from production incidents

  5. Test Error Scenarios: Regularly test how your application handles failures

By implementing comprehensive error handling strategies, you transform potential application failures into opportunities for improvement and create more robust, reliable software systems.