From 222a5e24bf03022b83b8e73e2511339452a1bd19 Mon Sep 17 00:00:00 2001 From: TekH Date: Sat, 11 Jul 2026 16:44:10 +0200 Subject: [PATCH] refactor(application): redesign exception hierarchy with flexible base class - Replace DEXJobException with JobException base class - Flexible params-based detail collection - Automatic message formatting with visual separators - Optional null-handling for contextual details - Add JobHttpException for HTTP client failures - Properties: ClientLibrary, ClientMethod - Use case: ReC API, REST requests - Add JobSqlException for SQL query failures - Property: Query (virtual for customization) - Use case: Main/Check query execution - Comprehensive XML documentation for all exception classes --- .../Common/Exceptions/DEXJobException.cs | 68 -------------- .../Common/Exceptions/JobException.cs | 91 +++++++++++++++++++ .../Common/Exceptions/JobHttpException.cs | 46 ++++++++++ .../Common/Exceptions/JobSqlException.cs | 46 ++++++++++ 4 files changed, 183 insertions(+), 68 deletions(-) delete mode 100644 ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs create mode 100644 ECMJobRunner.Application/Common/Exceptions/JobException.cs create mode 100644 ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs create mode 100644 ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs diff --git a/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs b/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs deleted file mode 100644 index 5cb974f..0000000 --- a/ECMJobRunner.Application/Common/Exceptions/DEXJobException.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; - -namespace ECMJobRunner.Application.Common.Exceptions -{ - /// - /// Exception thrown when a DEX job operation fails - /// - public class DEXJobException : Exception - { - /// - /// Initializes a new instance with an inner exception - /// - /// Name of the query that failed - /// Batch ID associated with the operation - /// SQL query that was executed (nullable) - /// The inner exception that caused the failure - public DEXJobException(string queryName, string batchId, string? sqlQuery, Exception innerException) - : base( - $"[SQL Execution Failure] Query '{queryName}' could not be completed for Batch '{batchId}'." + Environment.NewLine + - "─────────────────────────────────────────" + Environment.NewLine + - " Query:" + (sqlQuery is null ? string.Empty : Environment.NewLine + $" {sqlQuery}") + Environment.NewLine + Environment.NewLine + - " Root Cause:" + Environment.NewLine + - $" {innerException?.Message}" + Environment.NewLine + - "─────────────────────────────────────────", - innerException) - { - SqlQuery = sqlQuery; - BatchId = batchId; - QueryName = queryName; - } - - /// - /// Initializes a new instance with a reason message - /// - /// Name of the query that failed - /// Batch ID associated with the operation - /// SQL query that was executed (nullable) - /// Reason for the failure - public DEXJobException(string queryName, string batchId, string? sqlQuery, string reason) - : base( - $"[SQL Execution Failure] Query '{queryName}' could not be completed for Batch '{batchId}'." + Environment.NewLine + - "─────────────────────────────────────────" + Environment.NewLine + - " Query:" + (sqlQuery is null ? string.Empty : Environment.NewLine + $" {sqlQuery}") + Environment.NewLine + Environment.NewLine + - " Reason:" + Environment.NewLine + - $" {reason}" + Environment.NewLine + - "─────────────────────────────────────────") - { - SqlQuery = sqlQuery; - BatchId = batchId; - QueryName = queryName; - } - - /// - /// Gets the SQL query that failed (if available) - /// - public string? SqlQuery { get; } - - /// - /// Gets the batch ID associated with the operation - /// - public string BatchId { get; } - - /// - /// Gets the name of the query that failed - /// - public string QueryName { get; } - } -} diff --git a/ECMJobRunner.Application/Common/Exceptions/JobException.cs b/ECMJobRunner.Application/Common/Exceptions/JobException.cs new file mode 100644 index 0000000..0c89a86 --- /dev/null +++ b/ECMJobRunner.Application/Common/Exceptions/JobException.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; + +namespace ECMJobRunner.Application.Common.Exceptions +{ + /// + /// Base exception class for job execution failures + /// Provides a flexible structure for capturing job context and detailed error information + /// + public class JobException : Exception + { + /// + /// Initializes a new instance of JobException with detailed context information + /// + /// Name of the job that failed (e.g., "SQL Main Query", "ReC Request") + /// Name of the process/stage being executed (e.g., "MainQueryExecution", "CheckQueryValidation") + /// Unique batch identifier for tracking the execution + /// Human-readable reason for the failure (nullable) + /// The underlying exception that caused the failure (nullable) + /// Additional contextual details as name-value pairs with optional null-handling + /// + /// The details parameter accepts tuples with: + /// - Name: Display name of the detail + /// - Value: String value of the detail (nullable) + /// - IgnoreIfNull: If true, the detail is omitted from the message when value is null + /// + public JobException(string jobName, string processName, string batchId, string? reason, Exception? innerException, params (string Name, string? Value, bool IgnoreIfNull)[] details) + : base( + Message(jobName, + [ + ("Process Name", processName, false), + ("Batch Id", batchId, false), + ("Reason", reason, true), + ..details + ]), + innerException) + { + JobName = jobName; + ProcessName = processName; + BatchId = batchId; + } + + /// + /// Gets the name of the job that failed + /// + public string JobName { get; } + + /// + /// Gets the name of the process/stage that was being executed when the failure occurred + /// + public string ProcessName { get; } + + /// + /// Gets the unique batch identifier for tracking the execution + /// + public string BatchId { get; } + + /// + /// Generates a formatted error message with job context and details + /// + /// Name of the job that failed + /// Collection of name-value pairs with optional null-handling + /// Formatted multi-line error message with visual separators + /// + /// Message format: + /// + /// {jobName} could not be completed. + /// ───────────────────────────────────────── + /// Process Name: {processName} + /// Batch Id: {batchId} + /// {additional details...} + /// ───────────────────────────────────────── + /// + /// Details with IgnoreIfNull=true are omitted when their value is null. + /// + internal static string Message(string jobName, IEnumerable<(string Name, string? Value, bool IgnoreIfNull)> details) + { + var message = new System.Text.StringBuilder(); + message.AppendLine($"{jobName} could not be completed."); + message.AppendLine("─────────────────────────────────────────"); + foreach (var (name, value, ignoreNullValue) in details) + { + if (ignoreNullValue && value is null) + continue; + message.AppendLine($" {name}: {value}"); + } + message.AppendLine("─────────────────────────────────────────"); + return message.ToString(); + } + } +} \ No newline at end of file diff --git a/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs b/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs new file mode 100644 index 0000000..8243aff --- /dev/null +++ b/ECMJobRunner.Application/Common/Exceptions/JobHttpException.cs @@ -0,0 +1,46 @@ +using System; + +namespace ECMJobRunner.Application.Common.Exceptions +{ + /// + /// Exception for HTTP client-related job failures (e.g., ReC API calls, REST requests) + /// Extends JobException with client library and method context + /// + public class JobHttpException : JobException + { + /// + /// Initializes a new instance of JobHttpException with HTTP client context + /// + /// Name of the job that failed (e.g., "ReC Request", "API Call") + /// Name of the process/stage being executed + /// Unique batch identifier for tracking + /// Human-readable reason for the failure (nullable) + /// Name of the HTTP client library used (e.g., "ReC.Client", "HttpClient") (nullable) + /// Name of the client method that failed (e.g., "ExecuteAsync", "PostAsync") (nullable) + /// The underlying exception that caused the failure (nullable) + /// + /// Use this exception for HTTP-related failures such as: + /// - ReC API request failures + /// - REST API communication errors + /// - HTTP client timeout/network issues + /// - Authentication/authorization failures + /// + public JobHttpException(string jobName, string processName, string batchId, string? reason, string? clientLibrary, string? clientMethod, Exception? innerException) : base(jobName, processName, batchId, reason, innerException, + ("Client Library", clientLibrary, true), + ("Client Method", clientMethod, true)) + { + ClientLibrary = clientLibrary; + ClientMethod = clientMethod; + } + + /// + /// Gets the name of the HTTP client library that was used (e.g., "ReC.Client", "HttpClient") + /// + public string? ClientLibrary { get; } + + /// + /// Gets the name of the client method that failed (e.g., "ExecuteAsync", "PostAsync") + /// + public string? ClientMethod { get; } + } +} diff --git a/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs b/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs new file mode 100644 index 0000000..cde9537 --- /dev/null +++ b/ECMJobRunner.Application/Common/Exceptions/JobSqlException.cs @@ -0,0 +1,46 @@ +using System; + +namespace ECMJobRunner.Application.Common.Exceptions +{ + /// + /// Exception for SQL query execution failures + /// Extends JobException with SQL query context for debugging + /// + public class JobSqlException : JobException + { + /// + /// Initializes a new instance of JobSqlException with SQL query context + /// + /// Name of the job that failed (e.g., "Main Query Execution", "Check Query") + /// Name of the process/stage being executed + /// Unique batch identifier for tracking + /// Human-readable reason for the failure (nullable) + /// The SQL query that failed (nullable, for debugging purposes) + /// The underlying SQL exception (nullable) + /// + /// Use this exception for SQL-related failures such as: + /// - Main query execution errors + /// - Check query validation failures + /// - Database connection issues + /// - SQL syntax errors + /// - Query timeout exceptions + /// + /// The Query property can be logged for debugging but should be handled carefully + /// to avoid exposing sensitive data in production logs. + /// + public JobSqlException(string jobName, string processName, string batchId, string? reason, string? query, Exception? innerException) : base(jobName, processName, batchId, reason, innerException, + ("Query", query, true)) + { + Query = query; + } + + /// + /// Gets the SQL query that failed (nullable) + /// + /// + /// This property is marked as virtual to allow derived classes to customize query handling + /// (e.g., sanitizing sensitive data, truncating long queries) + /// + public virtual string? Query { get; } + } +}