Ian*_*oyd 51 .net c# exception tostring
抛出异常时(在IDE中进行调试时),我有机会查看异常的详细信息:

但在代码中,如果我打电话,exception.ToString()我不会看到这些有用的细节:
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'FetchActiveUsers'.
[...snip stack trace...]
Run Code Online (Sandbox Code Playgroud)
但Visual Studio有一些魔力可以将异常复制到剪贴板:

这给出了有用的细节:
System.Data.SqlClient.SqlException was unhandled by user code
Message=Could not find stored procedure 'FetchActiveUsers'.
Source=.Net SqlClient Data Provider
ErrorCode=-2146232060
Class=16
LineNumber=1
Number=2812
Procedure=""
Server=vader
State=62
StackTrace:
[...snip stack trace...]
InnerException:
Run Code Online (Sandbox Code Playgroud)
好吧,我想要那个!
会是什么内容:
String ExceptionToString(Exception ex)
{
//todo: Write useful routine
return ex.ToString();
}
Run Code Online (Sandbox Code Playgroud)
这可以实现同样的魔力.是否在某处内置了.NET功能?是否Exception有一个秘密的地方的方法将其转换为字符串?
jas*_*son 54
ErrorCode是针对ExternalException,不Exception和LineNumber和Number特定于SqlException,没有Exception.因此,从一般扩展方法获取这些属性的唯一方法Exception是使用反射来迭代所有公共属性.
所以你不得不说:
public static string GetExceptionDetails(this Exception exception) {
var properties = exception.GetType()
.GetProperties();
var fields = properties
.Select(property => new {
Name = property.Name,
Value = property.GetValue(exception, null)
})
.Select(x => String.Format(
"{0} = {1}",
x.Name,
x.Value != null ? x.Value.ToString() : String.Empty
));
return String.Join("\n", fields);
}
Run Code Online (Sandbox Code Playgroud)
(未对恭维问题进行测试.)
.NET 2.0兼容的答案:
public static string GetExceptionDetails(this Exception exception)
{
PropertyInfo[] properties = exception.GetType()
.GetProperties();
List<string> fields = new List<string>();
foreach(PropertyInfo property in properties) {
object value = property.GetValue(exception, null);
fields.Add(String.Format(
"{0} = {1}",
property.Name,
value != null ? value.ToString() : String.Empty
));
}
return String.Join("\n", fields.ToArray());
}
Run Code Online (Sandbox Code Playgroud)
Ger*_*art 21
我首先尝试了Jason的答案(在顶部),它的效果非常好,但我也想要:
我现在用这个:
public static void WriteExceptionDetails(Exception exception, StringBuilder builderToFill, int level)
{
var indent = new string(' ', level);
if (level > 0)
{
builderToFill.AppendLine(indent + "=== INNER EXCEPTION ===");
}
Action<string> append = (prop) =>
{
var propInfo = exception.GetType().GetProperty(prop);
var val = propInfo.GetValue(exception);
if (val != null)
{
builderToFill.AppendFormat("{0}{1}: {2}{3}", indent, prop, val.ToString(), Environment.NewLine);
}
};
append("Message");
append("HResult");
append("HelpLink");
append("Source");
append("StackTrace");
append("TargetSite");
foreach (DictionaryEntry de in exception.Data)
{
builderToFill.AppendFormat("{0} {1} = {2}{3}", indent, de.Key, de.Value, Environment.NewLine);
}
if (exception.InnerException != null)
{
WriteExceptionDetails(exception.InnerException, builderToFill, ++level);
}
}
Run Code Online (Sandbox Code Playgroud)
像这样打电话:
var builder = new StringBuilder();
WriteExceptionDetails(exception, builder, 0);
return builder.ToString();
Run Code Online (Sandbox Code Playgroud)
Muh*_*eed 10
这个全面的答案处理写出:
Data在所有异常中找到的集合属性(接受的答案不会这样做).InnerException(接受的答案不会这样做).AggregateException.它还以更好的顺序写出异常的属性.它使用的是C#6.0,但如果需要,您应该很容易转换为旧版本.
public static class ExceptionExtensions
{
public static string ToDetailedString(this Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
return ToDetailedString(exception, ExceptionOptions.Default);
}
public static string ToDetailedString(this Exception exception, ExceptionOptions options)
{
var stringBuilder = new StringBuilder();
AppendValue(stringBuilder, "Type", exception.GetType().FullName, options);
foreach (PropertyInfo property in exception
.GetType()
.GetProperties()
.OrderByDescending(x => string.Equals(x.Name, nameof(exception.Message), StringComparison.Ordinal))
.ThenByDescending(x => string.Equals(x.Name, nameof(exception.Source), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(exception.InnerException), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(AggregateException.InnerExceptions), StringComparison.Ordinal)))
{
var value = property.GetValue(exception, null);
if (value == null && options.OmitNullProperties)
{
if (options.OmitNullProperties)
{
continue;
}
else
{
value = string.Empty;
}
}
AppendValue(stringBuilder, property.Name, value, options);
}
return stringBuilder.ToString().TrimEnd('\r', '\n');
}
private static void AppendCollection(
StringBuilder stringBuilder,
string propertyName,
IEnumerable collection,
ExceptionOptions options)
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
var innerOptions = new ExceptionOptions(options, options.CurrentIndentLevel + 1);
var i = 0;
foreach (var item in collection)
{
var innerPropertyName = $"[{i}]";
if (item is Exception)
{
var innerException = (Exception)item;
AppendException(
stringBuilder,
innerPropertyName,
innerException,
innerOptions);
}
else
{
AppendValue(
stringBuilder,
innerPropertyName,
item,
innerOptions);
}
++i;
}
}
private static void AppendException(
StringBuilder stringBuilder,
string propertyName,
Exception exception,
ExceptionOptions options)
{
var innerExceptionString = ToDetailedString(
exception,
new ExceptionOptions(options, options.CurrentIndentLevel + 1));
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
stringBuilder.AppendLine(innerExceptionString);
}
private static string IndentString(string value, ExceptionOptions options)
{
return value.Replace(Environment.NewLine, Environment.NewLine + options.Indent);
}
private static void AppendValue(
StringBuilder stringBuilder,
string propertyName,
object value,
ExceptionOptions options)
{
if (value is DictionaryEntry)
{
DictionaryEntry dictionaryEntry = (DictionaryEntry)value;
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {dictionaryEntry.Key} : {dictionaryEntry.Value}");
}
else if (value is Exception)
{
var innerException = (Exception)value;
AppendException(
stringBuilder,
propertyName,
innerException,
options);
}
else if (value is IEnumerable && !(value is string))
{
var collection = (IEnumerable)value;
if (collection.GetEnumerator().MoveNext())
{
AppendCollection(
stringBuilder,
propertyName,
collection,
options);
}
}
else
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {value}");
}
}
}
public struct ExceptionOptions
{
public static readonly ExceptionOptions Default = new ExceptionOptions()
{
CurrentIndentLevel = 0,
IndentSpaces = 4,
OmitNullProperties = true
};
internal ExceptionOptions(ExceptionOptions options, int currentIndent)
{
this.CurrentIndentLevel = currentIndent;
this.IndentSpaces = options.IndentSpaces;
this.OmitNullProperties = options.OmitNullProperties;
}
internal string Indent { get { return new string(' ', this.IndentSpaces * this.CurrentIndentLevel); } }
internal int CurrentIndentLevel { get; set; }
public int IndentSpaces { get; set; }
public bool OmitNullProperties { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
大多数人都会使用此代码进行日志记录.考虑将Serilog与我的Serilog.Exceptions NuGet包一起使用,它还记录了异常的所有属性,但在大多数情况下更快,没有反射.Serilog是一个非常先进的日志框架,在撰写本文时风靡一时.
您可以使用Ben.Demystifier NuGet包为您的异常获取人类可读的堆栈跟踪,或者如果您使用Serilog,则可以使用serilog -enrichers-demystify NuGet包.如果您使用的是.NET Core 2.1,则内置此功能.
对于那些不想干扰覆盖的人来说,这种简单的非侵入式方法可能就足够了:
public static string GetExceptionDetails(Exception exception)
{
return "Exception: " + exception.GetType()
+ "\r\nInnerException: " + exception.InnerException
+ "\r\nMessage: " + exception.Message
+ "\r\nStackTrace: " + exception.StackTrace;
}
Run Code Online (Sandbox Code Playgroud)
它没有显示您想要的SQLException特定细节,但......
| 归档时间: |
|
| 查看次数: |
29730 次 |
| 最近记录: |