展示我完整
InnerException
的正确方式是什么。
我发现我的一些 InnerExceptions 还有另一个
InnerException
,而且这种情况非常严重。
InnerException.ToString()
会为我做这项工作吗?还是我需要循环遍历InnerExceptions
并用String
建立一个StringBuilder
?
您可以简单地打印
exception.ToString()
——这还将包括所有嵌套 InnerException
的全文。
我通常这样做来消除大部分噪音:
void LogException(Exception error) {
Exception realerror = error;
while (realerror.InnerException != null)
realerror = realerror.InnerException;
Console.WriteLine(realerror.ToString())
}
编辑:我忘记了这个答案,很惊讶没有人指出你可以这样做
void LogException(Exception error) {
Console.WriteLine(error.GetBaseException().ToString())
}
只需使用
exception.ToString()
https://learn.microsoft.com/en-us/dotnet/api/system.exception.tostring#remarks
ToString的默认实现获取抛出当前异常的类的名称、消息、对内部异常调用ToString的结果以及调用Environment.StackTrace的结果。如果这些成员中的任何一个为 null,则其值不包含在返回的字符串中。
如果没有错误信息或者为空字符串(“”),则不返回错误信息。仅当内部异常的名称和堆栈跟踪不为 null 时才会返回。
exception.ToString() 还将对该异常的内部异常调用 .ToString(),依此类推...
但是,在某些情况下,您可能只需要内部消息,对于这些情况,我使用以下扩展方法:
public static class ExceptionExtensions
{
public static string GetFullMessage(this Exception ex)
{
return ex.InnerException == null
? ex.Message
: ex.Message + " --> " + ex.InnerException.GetFullMessage();
}
}
当我有不同的监听器用于跟踪和记录并且想要对它们有不同的视图时,我经常使用这种方法。这样,我就可以拥有一个侦听器,通过电子邮件将带有堆栈跟踪的整个错误发送给开发团队,以便使用
.ToString()
方法进行调试,还有一个侦听器将每天发生的所有错误的历史记录写入日志文件,而无需使用使用
.GetFullMessage()
方法进行堆栈跟踪。
Message
部分,您可以执行以下操作:
public static string ToFormattedString(this Exception exception)
{
IEnumerable<string> messages = exception
.GetAllExceptions()
.Where(e => !String.IsNullOrWhiteSpace(e.Message))
.Select(e => e.Message.Trim());
string flattened = String.Join(Environment.NewLine, messages); // <-- the separator here
return flattened;
}
public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
{
yield return exception;
if (exception is AggregateException aggrEx)
{
foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
{
yield return innerEx;
}
}
else if (exception.InnerException != null)
{
foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
{
yield return innerEx;
}
}
}
这会递归地遍历所有内部异常(包括
AggregateException
的情况)以打印其中包含的所有
Message
属性,并以换行符分隔。例如
var outerAggrEx = new AggregateException(
"Outer aggr ex occurred.",
new AggregateException("Inner aggr ex.", new FormatException("Number isn't in correct format.")),
new IOException("Unauthorized file access.", new SecurityException("Not administrator.")));
Console.WriteLine(outerAggrEx.ToFormattedString());
发生外部聚合。内部聚合前。
号码格式不正确。
未经授权的文件访问。
不是管理员。
Exception 属性以获取更多详细信息。例如Data
会有一些信息。你可以这样做:
foreach (DictionaryEntry kvp in exception.Data)
要获取所有派生属性(不在基
Exception
类上),你可以这样做:
exception
.GetType()
.GetProperties()
.Where(p => p.CanRead)
.Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
namespace System {
public static class ExtensionMethods {
public static string FullMessage(this Exception ex) {
if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
var msg = ex.Message.Replace(", see inner exception.", "").Trim();
var innerMsg = ex.InnerException?.FullMessage();
if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
return msg;
}
}
}
这个“漂亮地打印”所有内部异常,并且还处理 AggregateException 以及 InnerException.Message 与 Message 相同的情况
exception.ToString()
不会为您提供
DbEntityValidationException
异常的详细信息。您可能想使用相同的方法来处理所有异常,例如:
catch (Exception ex)
{
Log.Error(GetExceptionDetails(ex));
}
其中 GetExceptionDetails
包含这样的内容:
public static string GetExceptionDetails(Exception ex)
{
var stringBuilder = new StringBuilder();
while (ex != null)
{
switch (ex)
{
case DbEntityValidationException dbEx:
var errorMessages = dbEx.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
var fullErrorMessage = string.Join("; ", errorMessages);
var message = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
stringBuilder.Insert(0, dbEx.StackTrace);
stringBuilder.Insert(0, message);
break;
default:
stringBuilder.Insert(0, ex.StackTrace);
stringBuilder.Insert(0, ex.Message);
break;
}
ex = ex.InnerException;
}
return stringBuilder.ToString();
}
exception.ToString()
。它将从所有内部异常收集数据。如果您只想要原始异常,请使用
exception.GetBaseException().ToString()
。这将为您带来第一个例外,例如如果没有内部异常,则为最深的内部异常或当前异常。示例:
try {
Exception ex1 = new Exception( "Original" );
Exception ex2 = new Exception( "Second", ex1 );
Exception ex3 = new Exception( "Third", ex2 );
throw ex3;
} catch( Exception ex ) {
// ex => ex3
Exception baseEx = ex.GetBaseException(); // => ex1
}
使用他的答案时缺少变量 aggrEx,我添加了它。
文件 ExceptionExtensions.class:
// example usage:
// try{ ... } catch(Exception e) { MessageBox.Show(e.ToFormattedString()); }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace YourNamespace
{
public static class ExceptionExtensions
{
public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
{
yield return exception;
if (exception is AggregateException )
{
var aggrEx = exception as AggregateException;
foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
{
yield return innerEx;
}
}
else if (exception.InnerException != null)
{
foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
{
yield return innerEx;
}
}
}
public static string ToFormattedString(this Exception exception)
{
IEnumerable<string> messages = exception
.GetAllExceptions()
.Where(e => !String.IsNullOrWhiteSpace(e.Message))
.Select(exceptionPart => exceptionPart.Message.Trim() + "\r\n" + (exceptionPart.StackTrace!=null? exceptionPart.StackTrace.Trim():"") );
string flattened = String.Join("\r\n\r\n", messages); // <-- the separator here
return flattened;
}
}
}
public static string GetCompleteMessage(this Exception error)
{
System.Text.StringBuilder builder = new StringBuilder();
Exception realerror = error;
builder.AppendLine(error.Message);
while (realerror.InnerException != null)
{
builder.AppendLine(realerror.InnerException.Message);
realerror = realerror.InnerException;
}
return builder.ToString();
}
HTML
表示:
const string _HTML_TAB = " ";
public static string ToHtmlString(this Exception ex, int level = 0)
{
string message = GetText("Message", ex.Message, level);
if (ex.InnerException != null && level < 30)
{
message += ToHtmlString(ex.InnerException, level + 1);
}
else
{
message += GetText("StackTrace", ex.StackTrace, level); ;
message += GetText("Source", ex.Source, level); ;
message += GetText("TargetSite", ex.TargetSite.ToString(), level);
}
return message;
}
private static string GetText(string headline, string text, int level)
{
var indentText = string.Join(_HTML_TAB, new string[level + 1]);
var newLine = $"<br />{indentText}{_HTML_TAB}";
return $"{indentText}<b>{headline}</b>{newLine}"
+ $"{text.Replace(Environment.NewLine, newLine)}<br /><br />";
}
catch
子句中使用此代码:
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
if (e is TaskCanceledException taskException)
Console.WriteLine("Unable to compute mean: {taskException.Message}");
if (e is OperationCanceledException operationException)
Console.WriteLine($"Unable to compute mean: {operationException.Message}");
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}