totitlecase initcap first convert capital camelcase c# .net exception tostring

initcap - uppercase c#



.NET: ¿Cómo convertir Exception a string? (10)

Cuando se lanza una excepción (mientras se depura en el IDE), tengo la oportunidad de ver los detalles de la excepción:

Pero en el código si llamo exception.ToString() no consigo ver esos detalles útiles:

System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure ''FetchActiveUsers''. [...snip stack trace...]

Pero Visual Studio tiene algo de magia donde puede copiar la excepción al portapapeles :

Que da los detalles útiles:

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:

Bueno, yo quiero eso!

¿Cuál sería el contenido de:

String ExceptionToString(Exception ex) { //todo: Write useful routine return ex.ToString(); }

eso puede lograr la misma magia. ¿Hay alguna función .NET incorporada en alguna parte? ¿ Exception tiene un método secreto en algún lugar para convertirlo en una cadena?



En Visual Studio, ese tipo de información puede ser generada por un visualizador de depuración.

Supongo que porque es posible escribir su propio visualizador de depuración: http://msdn.microsoft.com/en-us/library/e2zc529c.aspx

Eso en teoría, si puede aplicar ingeniería inversa al visualizador de depurador incorporado para las excepciones (si puede determinar dónde están almacenadas), entonces podría usar la misma funcionalidad.

EDITAR:

Aquí hay una publicación sobre dónde se guardan los visualizadores del depurador: ¿Dónde encuentro Microsoft.VisualStudio.DebuggerVisualizers?

Es posible que pueda usarlo para sus propios fines.


Esta respuesta integral maneja la escritura:

  1. La propiedad de recopilación de Data encontrada en todas las excepciones (la respuesta aceptada no hace esto).
  2. Cualquier otra propiedad personalizada agregada a la excepción.
  3. Escribe recursivamente InnerException (La respuesta aceptada no hace esto).
  4. Escribe la colección de excepciones contenidas en AggregateException .

También escribe las propiedades de las excepciones en un orden más agradable. Está utilizando C # 6.0, pero debería ser muy fácil para usted convertirlo a versiones anteriores si es necesario.

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; } }

Sugerencias - Excepciones de registro

La mayoría de las personas usará este código para iniciar sesión. Considere usar Serilog con mi paquete Serilog.Exceptions NuGet, que también registra todas las propiedades de una excepción, pero lo hace más rápido y sin reflejos en la mayoría de los casos. Serilog es un framework de registro muy avanzado que está de moda en el momento de escribir.

Sugerencias - Rastreos de pila legibles por humanos

Puede usar el paquete Ben.Demystifier NuGet para obtener rastros de pila legibles por humanos para sus excepciones o el serilog-enrichers-demystify demystify si está usando Serilog.


No hay un método secreto Probablemente pueda anular el método ToString() y crear la cadena que desee.

Cosas como ErrorCode y Message son solo propiedades de la excepción que puede agregar a la salida de cadena deseada.

Actualización: después de volver a leer su pregunta y pensar más sobre esto, es más probable que la answer de Jason sea lo que usted desea. ToString() método ToString() solo sería útil para las excepciones que haya creado, pero que ya no estén implementadas. No tiene sentido clasificar las excepciones existentes solo para agregar esta funcionalidad.


Para las personas que no quieren meterse con la anulación, este simple método no intrusivo podría ser suficiente:

public static string GetExceptionDetails(Exception exception) { return "Exception: " + exception.GetType() + "/r/nInnerException: " + exception.InnerException + "/r/nMessage: " + exception.Message + "/r/nStackTrace: " + exception.StackTrace; }

No muestra los detalles específicos de SQLException que desea, aunque ...


Para mostrar algunos detalles al usuario, debe usar ex.Message . Para mostrar a los desarrolladores es probable que necesites ex.Message y ex.StackTrace .

No hay un método "secreto", podría considerar que la propiedad Mensaje es la más adecuada para mensajes fáciles de usar.

También tenga cuidado de que, en algunos casos, tenga una excepción interna en la excepción que capture, que también sería útil para iniciar sesión.


Primero probé la respuesta de Jason (en la parte superior), que funcionó bastante bien, pero también quería:

  • Para iterar iterativamente a través de excepciones internas y sangrarlas.
  • Ignore las propiedades nulas y aumenta la legibilidad de la salida.
  • Incluye los metadatos en la propiedad de Datos. (si corresponde) pero excluye la propiedad Data en sí misma. (es inutil).

Ahora uso esto:

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); } }

Llamar así:

var builder = new StringBuilder(); WriteExceptionDetails(exception, builder, 0); return builder.ToString();


Probablemente tendrá que construir manualmente esa cadena al concatenar los diversos campos que le interesan.


Si llama a ToString en el objeto Excepción, obtendrá el nombre de la clase adjuntado por el mensaje, seguido de una excepción interna y luego el seguimiento de la pila.

className + message + InnerException + stackTrace

Dado que InnerException y StackTrace solo se agregan si no son nulos. Además, los campos que ha mencionado en la captura de pantalla no son parte de la clase Excepción estándar. Sí, la excepción sí ofrece una propiedad pública llamada "Datos", que contiene información adicional definida por el usuario sobre la excepción.


ErrorCode es específico de ExternalException , no Exception y LineNumber and Number son específicos de SqlException , no de Exception . Por lo tanto, la única forma de obtener estas propiedades de un método de extensión general en Exception es usar la reflexión para iterar sobre todas las propiedades públicas.

Entonces, tendrás que decir algo como:

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); }

(No probado para problemas de compilación).

Respuesta compatible con .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()); }