2010-08-31 9 views
8

Aunque me doy cuenta de que hay una pregunta similar (How to serialize an Exception object in C#?), y aunque las respuestas en esa página fueron útiles, no resolvieron el problema ni respondieron exactamente la pregunta planteada.Serialización Excepción para ser arrojable

Creo que la pregunta era cómo serializar el objeto para permitir que se reconstruya (deserialice) en el mismo objeto. Intenté utilizar la solución proporcionada por davogones y Antony Booth, pero sin agregar la clase base System.Exception en el lado consumidor (como en: SerializationException: Exception), es imposible usar estos tipos (por sí mismos) como objetos de excepción reales que pueden ser arrojado.

Antes de continuar, permítanme explicar esa última afirmación. Intenté usar Antony Booth's solution en un servicio web (el servicio contiene la definición del objeto serializable) para que todos los consumidores utilicen la misma excepción (con la esperanza de crear un tipo de excepción serializable reutilizable en lugar de volver a crearlo).

Lamentablemente, dado que ninguno de los tipos se deriva explícitamente de System.Exception, no puede throw, lo que obviamente sería útil. Como mencioné anteriormente, parece que agregar : Exception a la definición de clase de tipo en el lado consumidor permite lanzar el objeto, pero eso requiere editar el código WSDL/servicio web generado automáticamente, que parece intuitivamente como un mal/no sostenible practica conmigo (corrígeme si estoy equivocado).

Mi primera pregunta es, de nuevo, ¿es posible serializar System.Exception o crear un tipo derivado que se pueda serializar y, si es posible, cómo se podría hacer? Debo mencionar que he visto lo que parece ser la forma oficial de reconstitute the Exception object, pero me temo que no lo entiendo muy bien.

Mi segunda pregunta es sobre la arquitectura de System.Exception sí mismo. Lo que me gustaría saber es por qué el tipo System.Exception está marcado como [Serializable] cuando ha sido documentado y aparentemente diseñado para no permitirle serializarlo correctamente (al menos con XML) porque el objeto Data implementa IDictionary?

De MSDN:

Q: ¿Por qué no puedo serializar tablas hash?

A: El XmlSerializer no puede procesar las clases que implementan la interfaz IDictionary. Esto se debió en parte a restricciones de programación y en parte debido al hecho de que una tabla hash no tiene una contraparte en el sistema de tipo XSD. La única solución es implementar una tabla hash personalizada que no implemente la interfaz IDictionary.

Dado que XML se está convirtiendo (si no está ya) un nuevo estándar para el transporte de datos (recomendado oficialmente por Microsoft, no obstante), parece absurdo estúpido para no permitir que el único tipo de objeto en .NET que puede ser lanzado para no ser XML-serializable.

Espero escuchar algunos pensamientos de todos los SO'rs (sobre todo porque esta es mi primera publicación).

Si tiene preguntas o necesita aclaraciones, no dude en hacérmelo saber.


Nota: Me acaba de encontrar this SO post, que parece responder a algunas preguntas, pero supongo que me gustaría tener mi propio golpe en ella. pero dígame si está demasiado cerca de un duplicado.

Respuesta

1

Puede crear una clase derivada de Exception y realizar la serialización y la deserialización mediante la implementación de la interfaz ISerializable.

ejemplo tomado de wrox forums, subclases ApplicationException:

EDITAR: Como se ha señalado, ApplicationException está en desuso. Usar la clase base Exception debería funcionar bien.

using System; 
using System.Collections; 
using System.Runtime.Serialization; 

namespace Common.CustomExceptions 
{ 

    /// <summary> 
    /// Custom exception. 
    /// </summary> 
    [Serializable] 
    public class CustomExceptionBase: ApplicationException 
     { 

     // Local private members 
     protected DateTime _dateTime = DateTime.Now; 
     protected String _machineName = Environment.MachineName; 
     protected String _exceptionType = ""; 
     private String _exceptionDescription = ""; 
     protected String _stackTrace = ""; 
     protected String _assemblyName = ""; 
     protected String _messageName = ""; 
     protected String _messageId = ""; 
     protected Hashtable _data = null; 
     protected String _source = ""; 
     protected Int32 _exceptionNumber = 0; 

     public CustomExceptionBase(): base() 
     { 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 

     public CustomExceptionBase(Int32 exceptionNumber): base() 
     { 
      this._exceptionNumber = exceptionNumber; 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 

     public CustomExceptionBase(Int32 exceptionNumber, String message): base(message) 
     { 
      this._exceptionNumber = exceptionNumber; 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 

     public CustomExceptionBase(Int32 exceptionNumber, String message, Exception innerException): 
      base(message, innerException) 
     { 
      this._exceptionNumber = exceptionNumber; 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 

     public CustomExceptionBase(Int32 exceptionNumber, String message, Exception innerException, String messageName, String mqMessageId): 
      base(message, innerException) 
     { 
      this._exceptionNumber = exceptionNumber; 
      this._messageId = mqMessageId; 
      this._messageName = messageName; 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 

     public CustomExceptionBase(Int32 exceptionNumber, String message, Exception innerException, String messageName, String mqMessageId, String source): 
      base(message, innerException) 
     { 
      this._exceptionNumber = exceptionNumber; 
      this._messageId = mqMessageId; 
      this._messageName = messageName; 
      this._source = source.Equals("") ? this._source : source; 
      if (Environment.StackTrace != null) 
       this._stackTrace = Environment.StackTrace; 
     } 


     #region ISerializable members 

     /// <summary> 
     /// This CTor allows exceptions to be marhalled accross remoting boundaries 
     /// </summary> 
     /// <param name="info"></param> 
     /// <param name="context"></param> 
     protected CustomExceptionBase(SerializationInfo info, StreamingContext context) : 
      base(info,context) 
     { 
      this._dateTime = info.GetDateTime("_dateTime"); 
      this._machineName = info.GetString("_machineName"); 
      this._stackTrace = info.GetString("_stackTrace"); 
      this._exceptionType = info.GetString("_exceptionType"); 
      this._assemblyName = info.GetString("_assemblyName"); 
      this._messageName = info.GetString("_messageName"); 
      this._messageId = info.GetString("_messageId"); 
      this._exceptionDescription = info.GetString("_exceptionDescription"); 
      this._data = (Hashtable)info.GetValue("_data", Type.GetType("System.Collections.Hashtable")); 
     } 

     public override void GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
      info.AddValue("_dateTime", this._dateTime); 
      info.AddValue("_machineName", this._machineName); 
      info.AddValue("_stackTrace", this._stackTrace); 
      info.AddValue("_exceptionType", this._exceptionType); 
      info.AddValue("_assemblyName", this._assemblyName); 
      info.AddValue("_messageName", this._messageName); 
      info.AddValue("_messageId", this._messageId); 
      info.AddValue("_exceptionDescription", this._exceptionDescription); 
      info.AddValue("_data", this._data, Type.GetType("System.Collections.Hashtable")); 
      base.GetObjectData (info, context); 
     } 

     #endregion 
    } 
} 
+1

Solo una nota rápida que 'ApplicationException' ha quedado en desuso y _no debe_ usarse ni subclasificarse. Use 'Exception' en su lugar. – thecoop

+0

Desafortunadamente, esto no parece funcionar en el contexto de los servicios web. Copié tu código y cambié 'ApplicationException' a' Exception', y obtuve el mismo código de error al que me refería antes: No se puede serializar el miembro System.Exception.Data de tipo System.Collections.IDictionary, porque implementa IDictionary. Simplemente agregando '[Serializable]' no es suficiente. – llaughlin

+0

Hrm. ¿Tal vez puede cambiar el miembro _data a una colección serializable que no implementa IDictionary? Hay algunas implementaciones que hacen esto. –

0

Considere la posibilidad de tener dos clases.

La primera será la clase serializable ExceptionDescription, que debe implementar los atributos que desea serializar, y hereda de nada. El segundo es CustomException, que heredará de Exception y tendrá una única referencia a una instancia de ExceptionDescription. Además, CustomException implementará "operador implícito estático público" para que pueda moverse de forma natural entre las dos implementaciones.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Diagnostics; 
using System.Xml.Serialization; 

namespace SerializableException { 


public class CustomException : Exception { 


    public CustomException(ExceptionDescription d) { 
     this.description = d; 
    }//method 


    public CustomException(String message, Exception e) { 
     this.description = new ExceptionDescription(message, e, 2); 
    }//method 

    public CustomException(String message, Exception e, int stackDepth) { 
     this.description = new ExceptionDescription(message, e, stackDepth + 1); 
    }//method 


    public CustomException(String message, IEnumerable<Exception> causes) { 
     this.description = new ExceptionDescription(message, causes, 2); 
    }//method 


    public CustomException(String message, IEnumerable<Exception> causes, int stackDepth) { 
     this.description = new ExceptionDescription(message, causes, stackDepth + 1); 
    }//method 


    public CustomException(String message) { 
     this.description = new ExceptionDescription(message, 2); 
    }//method 


    public CustomException(String message, int stackDepth) { 
     this.description = new ExceptionDescription(message, stackDepth + 1); 
    }//method 


    public CustomException() { 
    }//method 


    public static CustomException newInstance(Exception e) { 
     if (e == null) return null; 
     if (e is CustomException) return (CustomException)e; 

     CustomException output = new CustomException(); 
     output.description = ExceptionDescription.newInstance(e); 
     return output; 
    }//method 


    public static implicit operator ExceptionDescription(CustomException e) { 
     if (e == null) return null; 
     return e.description; 
    }//method 

    public static implicit operator CustomException(ExceptionDescription d) { 
     return d == null ? null : new CustomException(d); 
    }//method 


    public ExceptionDescription description; 



    public String RawStackTrace { 
     get { return description.RawStackTrace; } 
     //set { rawStackTrace = value; } 
    }//method 


    public DateTime Time { 
     get { return description.Time; } 
    }//method 

    public override String Message { 
     get { return description.Message; } 
    }//method 


}//class 




[XmlRoot] 
public class ExceptionDescription { 

    public ExceptionDescription() { 
    }//method 


    public ExceptionDescription(String message, Exception cause, int stackDepth) { 
     this.Message = message; 
     this.Time = DateTime.Now; 
     this.RawStackTrace = new StackTrace(1 + stackDepth, true).ToString(); 
     this.Causes = new ExceptionDescription[] { ExceptionDescription.newInstance(cause) }; 
    }//method 



    public ExceptionDescription(String message, IEnumerable<Exception> causes, int stackDepth) { 
     this.Message = message; 
     this.Time = DateTime.Now; 
     this.RawStackTrace = new StackTrace(1 + stackDepth, true).ToString(); 
     this.Causes = (from Exception e in causes select ExceptionDescription.newInstance(e)).ToArray(); 
    }//method 


    public ExceptionDescription(String message, int stackDepth) { 
     this.Message = message; 
     this.Time = DateTime.Now; 
     this.RawStackTrace = new StackTrace(stackDepth + 1, true).ToString(); 
     this.Causes = new ExceptionDescription[0]; 
    }//method 



    public static ExceptionDescription newInstance(Exception e) { 
     if (e == null) return null; 
     if (e is CustomException) return ((CustomException)e).description; 

     ExceptionDescription output = new ExceptionDescription(); 
     output.Time = DateTime.Now; 
     output.Message = e.Message; 
     output.RawStackTrace = e.StackTrace; 

     if (e.InnerException != null) { 
      output.Causes = new ExceptionDescription[] { ExceptionDescription.newInstance(e.InnerException) }; 
     } else { 
      output.Causes = new ExceptionDescription[0]; 
     }//endif 
     return output; 
    }//method 





    public String Message; 
    public ExceptionDescription[] Causes;  //MORE THAN ONE CAUSE IS LEGITIMATE    
    public String RawStackTrace; 
    public DateTime Time; 



}//class 



}//namespace 
Cuestiones relacionadas