Skip to main content
added 275 characters in body
Source Link
t3chb0t
  • 44.7k
  • 9
  • 85
  • 191
public class UserNotFoundException : SmartException
{
    public UserNotFoundException(Exception innerException, [CallerMemberName] string callerName = "") : base(innerException)
    {
        CallerName = callerName;
    }

    public string CallerName
    {
        get { return GetValue<string>(); }
        private set { SetValue(value); }
    }

    public string UserName
    {
        get { return GetValue<string>(); }
        set { SetValue(value); }
    }
}
UserNotFound: CallerName = "Foo" UserName = "TestUser" >>> FileNotFound: FileName = "helloworld.txt"
public class UserNotFoundException : SmartException
{
    public UserNotFoundException(Exception innerException) : base(innerException) {}

    public string UserName
    {
        get { return GetValue<string>(); }
        set { SetValue(value); }
    }
}
UserNotFound: UserName = "TestUser" >>> FileNotFound: FileName = "helloworld.txt"
public class UserNotFoundException : SmartException
{
    public UserNotFoundException(Exception innerException, [CallerMemberName] string callerName = "") : base(innerException)
    {
        CallerName = callerName;
    }

    public string CallerName
    {
        get { return GetValue<string>(); }
        private set { SetValue(value); }
    }

    public string UserName
    {
        get { return GetValue<string>(); }
        set { SetValue(value); }
    }
}
UserNotFound: CallerName = "Foo" UserName = "TestUser" >>> FileNotFound: FileName = "helloworld.txt"
Source Link
t3chb0t
  • 44.7k
  • 9
  • 85
  • 191

"Better" Exception with autogenerated message

Because I don't like writing the same useless exeption messages all the time ;-) I thought I create a better exception that would make this for me. This is what I came up with:

[Serializable]
public class SmartException : Exception
{
    private const string PropertyNamePrefix = "$";
    private const string InnerExceptionSeparator = " >>> ";

    protected SmartException() { }

    protected SmartException(Exception innerException) : base(null, innerException) { }

    public override string Message => FormatMessage();

    protected T GetValue<T>([CallerMemberName] string propertyName = "")
    {
        return (T)Data[PropertyNamePrefix + propertyName];
    }

    protected void SetValue<T>(T value, [CallerMemberName] string propertyName = "")
    {
        Data[PropertyNamePrefix + propertyName] = value;
    }

    private string FormatMessage()
    {
        var ex = (Exception)this;

        var message = new StringBuilder(1024);


        while (ex != null)
        {
            var exceptionSeparator = message.Length > 0 ? InnerExceptionSeparator : string.Empty;
            var exceptionName = Regex.Replace(ex.GetType().Name, "Exception$", string.Empty);
            message.Append($"{exceptionSeparator}{exceptionName}");

            var properties = FormatProperties(ex);
            message.Append(string.IsNullOrEmpty(properties) ? null : $": {properties}");

            ex = ex.InnerException;
        }

        return message.ToString();
    }

    private static string FormatProperties(Exception ex)
    {
        return
            ex is SmartException
            ? FormatPropertiesFromData(ex)
            : FormatPropertiesFromType(ex);
    }

    private static string FormatPropertiesFromData(Exception ex)
    {
        var entries = ex.Data
            .Cast<DictionaryEntry>()
            .Where(de =>
                de.Key is string &&
                ((string)de.Key).StartsWith("$") &&
                de.Value != null);

        var properties = new StringBuilder(1024);
        foreach (var entry in entries)
        {
            var separator = properties.Length > 0 ? " " : string.Empty;
            var propertyName = Regex.Replace(
                entry.Key.ToString(), 
                $"^[{PropertyNamePrefix}]", 
                string.Empty);

            properties.Append($"{separator}{propertyName} = \"{entry.Value}\"");
        }

        return properties.ToString();
    }

    private static string FormatPropertiesFromType(Exception ex)
    {
        var propertyInfos =
            ex.GetType()
            .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.Name != "Message");

        var properties = new StringBuilder(1024);

        foreach (var propertyInfo in propertyInfos)
        {
            var value = propertyInfo.GetValue(ex);
            if (value == null)
            {
                continue;
            }
            var separator = properties.Length > 0 ? " " : string.Empty;
            properties.Append($"{separator}{propertyInfo.Name} = \"{value}\"");
        }

        return properties.ToString();
    }
}

Usage

public class UserNotFoundException : SmartException
{
    public UserNotFoundException(Exception innerException) : base(innerException) {}

    public string UserName
    {
        get { return GetValue<string>(); }
        set { SetValue(value); }
    }
}

and for:

var innerEx = new FileNotFoundException(null, "helloworld.txt");
var ex = new UserNotFoundException(innerEx) { UserName = "TestUser" };

the message would be:

UserNotFound: UserName = "TestUser" >>> FileNotFound: FileName = "helloworld.txt"

What it does is cutting-off the Exception from the name of the exception type and appending the properties to the message together with all inner exceptions.