using System; using System.Collections.Generic; using System.Linq; using Quadarax.Foundation.Core.Attributes; using Quadarax.Foundation.Core.Value.Extensions; namespace Quadarax.Foundation.Core.Exceptions { /// /// Provides a base class for exceptions that include a code. /// [Serializable] public class CodeException : Exception { public int Code => this.GetCode(); public CodeException(int code, string message, Exception innerException) : base(message, innerException) { this.SetCode(code); } public CodeException(int code, string message) : base(message) { this.SetCode(code); } public CodeException(int code, string message, params object[] messageArguments) : base(string.Format(message, messageArguments)) { this.SetCode(code); } public CodeException(int code, string message, Exception innerException, params object[] messageArguments) : base(string.Format(message, messageArguments), innerException) { this.SetCode(code); } } /// /// Provides a base class for exceptions that include a code in enumeration form. /// Exception message is resolved from the of the enumeration. /// /// Enumeration that defines error codes end its message defined by . public class CodeException : CodeException where TCodeEnum : struct, Enum { public new TCodeEnum Code => (TCodeEnum)Enum.ToObject(typeof(TCodeEnum), this.GetCode()); /// /// Static cache for error messages. /// private static readonly IDictionary _messageCache = new Dictionary(); public CodeException(TCodeEnum code, Exception innerException) : base(Convert.ToInt32(code), ResolveMessage(code), innerException) { } public CodeException(TCodeEnum code) : base(Convert.ToInt32(code), ResolveMessage(code)) { } public CodeException(TCodeEnum code, params object[] messageArguments) : base( Convert.ToInt32(code), ResolveMessage(code, messageArguments)) { } public CodeException(TCodeEnum code, Exception innerException, params object[] messageArguments) : base(Convert.ToInt32(code),ResolveMessage(code,messageArguments), innerException) { } private static string ResolveMessage(TCodeEnum code, params object[] messageArguments) { var codeValue = Convert.ToInt32(code); // check if cache is initialized if (_messageCache.Count == 0) { // initialize cache var fields = typeof(TCodeEnum).GetFields(); var names = Enum.GetNames(typeof(TCodeEnum)).ToArray(); var values = Enum.GetValues(typeof(TCodeEnum)); // iterate through all fields of the enumeration foreach (var field in fields) { var attr = field.GetCustomAttributes(typeof(ErrorMessageAttribute), false); var index = Array.IndexOf(names, field.Name); if (index<0) continue; var fieldVal = (int) (values.GetValue(index) ?? -1); // if value has attribute ErrorMessageAttribute, add it to the cache if (attr.Length == 1) { var message = ((ErrorMessageAttribute)attr[0]).ErrorMessage; _messageCache.Add(fieldVal, message); } else { // message not found, log warning and add default message to the cache System.Diagnostics.Debug.WriteLine($"Error message in enumeration '{typeof(TCodeEnum).Name}' not found for code '{codeValue}'", "WARN"); _messageCache.Add(fieldVal, $"Error message code: {code} [{fieldVal}]"); } } } // return message from cache return string.Format(_messageCache[codeValue], messageArguments); } } }