Сериализация исключения для выброса

Хотя я понимаю, что есть аналогичный вопрос (Как сериализовать объект исключения в C #?), и хотя ответы на этой странице были полезными, они не совсем решали проблему или не отвечали на поставленный вопрос.

Я считаю, что вопрос заключался в том, как сериализовать объект, чтобы он мог быть реконструирован (десериализован) в тот же объект. Я попытался использовать решение, данное davogones и Энтони Бут, но без добавления базового класса System.Exception на стороне потребителя (как в: SerializationException: Exception) невозможно использовать эти типы (сами по себе) в качестве фактических объектов исключения, которые могут быть выброшены.

Прежде чем я продолжу, позвольте мне объяснить это последнее утверждение. Я пробовал использовать решение Энтони Бута в веб-службе (служба содержит определение сериализуемого объекта) в попытке заставить всех потребителей использовать одно и то же исключение (мы надеемся создать многоразовый сериализуемый тип исключения вместо его повторного создания).

К сожалению, поскольку ни один из типов не наследуется явно от System.Exception, вы не можете throw их, что, очевидно, было бы полезно. Как я уже упоминал выше, кажется, что добавление : Exception к определению класса типа на стороне потребителя позволяет выбросить объект, но для этого требуется редактировать автоматически сгенерированный код WSDL / веб-службы, который интуитивно кажется плохим / необслуживаемым. потренируйтесь для меня (поправьте меня, если я ошибаюсь).

Мой первый вопрос, опять же, можно ли сериализовать System.Exception или создать производный тип, который можно сериализовать, и, если это возможно, как бы это сделать? Я должен упомянуть, что я изучил то, что кажется официальным способом воссоздать Exception объект, но, боюсь, я не очень хорошо его понимаю.

Второй вопрос касается архитектуры самого System.Exception. Я хотел бы знать, почему тип System.Exception помечен как [Serializable], когда он задокументирован и, по-видимому, предназначен для того, чтобы не допустить его правильной сериализации (по крайней мере, с помощью XML), потому что он Data объект реализует IDictionary?

Из MSDN:

В: Почему я не могу сериализовать хэш-таблицы?

A: XmlSerializer не может обрабатывать классы, реализующие интерфейс IDictionary. Отчасти это произошло из-за ограничений расписания, а отчасти из-за того, что хеш-таблица не имеет аналога в системе типов XSD. Единственное решение - реализовать настраиваемую хеш-таблицу, которая не реализует интерфейс IDictionary.

Учитывая, что XML становится (если еще не стал) новым стандартом передачи данных (тем не менее, официально рекомендованным Microsoft), кажется абсурдным глупым не допускать, чтобы единственный тип объекта в .NET, который может быть выброшен, не был сериализуемым в формате XML. .

Я с нетерпением жду некоторых мыслей от всех SO'ов (тем более, что это мой первый пост).

Если у вас есть вопросы или вам нужны разъяснения, не стесняйтесь, дайте мне знать.


Примечание. Я только что нашел это сообщение SO, который, кажется, отвечает на несколько вопросов, но я думаю, что хотел бы по-своему. дайте мне знать, если это слишком близко к дубликату.


person llaughlin    schedule 31.08.2010    source источник


Ответы (2)


Вы можете создать класс, производный от Exception, и самостоятельно выполнить сериализацию и десериализацию, реализовав ISerializable интерфейс.

Пример взят из форумов wrox, подкласс ApplicationException:

РЕДАКТИРОВАТЬ: как указано, ApplicationException устарел. Использование базового класса Exception должно работать нормально.

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
    }
}
person axel_c    schedule 31.08.2010
comment
Просто небольшое примечание, что ApplicationException устарел и не должен не использоваться или разделяться на подклассы. Вместо этого используйте Exception. - person thecoop; 31.08.2010
comment
К сожалению, это не работает в контексте веб-сервисов. Я скопировал ваш код и изменил ApplicationException на Exception и получил тот же код ошибки, о котором говорил раньше: не удается сериализовать член System.Exception.Data типа System.Collections.IDictionary, поскольку он реализует IDictionary. Просто добавить [Serializable] недостаточно. - person llaughlin; 31.08.2010
comment
Грм. Может быть, вы можете изменить член _data на сериализуемую коллекцию, которая не реализует IDictionary? Есть несколько реализаций, которые это делают. - person axel_c; 01.09.2010
comment
Это не работает, поскольку сериализатор xml и сериализатор контракта данных не могут сериализовать этот экземпляр, поэтому он в основном непригоден. - person Jim; 06.10.2011

Рассмотрите возможность создания двух классов.

Первым будет сериализуемый класс ExceptionDescription, который должен реализовывать атрибуты, которые вы хотите сериализовать, и наследует от ничего. Второй - CustomException, который наследуется от Exception и имеет единственную ссылку на экземпляр ExceptionDescription. Кроме того, CustomException будет реализовывать «общедоступный статический неявный оператор», поэтому вы можете естественным образом перемещаться между двумя реализациями.

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
person Kyle Lahnakoski    schedule 25.10.2010