Как сериализовать объект исключения в C#?


Я пытаюсь сериализовать объект исключения в C#. Однако, похоже, что это невозможно, так как класс исключений не помечен как [Serializable]. Есть ли способ обойти это?

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

мой первый рефлекс-сериализовать его.

9 67

9 ответов:

то, что я сделал раньше, это создать пользовательский класс ошибок. Это инкапсулирует всю соответствующую информацию об исключении и является сериализуемым XML.

[Serializable]
public class Error
{
    public DateTime TimeStamp { get; set; }
    public string Message { get; set; }
    public string StackTrace { get; set; }

    public Error()
    {
        this.TimeStamp = DateTime.Now;
    }

    public Error(string Message) : this()
    {
        this.Message = Message;
    }

    public Error(System.Exception ex) : this(ex.Message)
    {
        this.StackTrace = ex.StackTrace;
    }

    public override string ToString()
    {
        return this.Message + this.StackTrace;
    }
}

создайте пользовательский класс исключений с помощью [Serializable ()]. Вот пример, взятый из MSDN:

[Serializable()]
public class InvalidDepartmentException : System.Exception
{
    public InvalidDepartmentException() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }

    // Constructor needed for serialization 
    // when exception propagates from a remoting server to the client.
    protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}

класс исключений и помечен как сериализуемый и реализует ISerializable. см. MSDN:http://msdn.microsoft.com/en-us/library/system.exception.aspx

Если вы пытаетесь сериализовать в XML с помощью XmlSerializer, вы нажмете ошибку на всех членах, которые реализуют IDictionary. Это ограничение XmlSerializer, но класс, безусловно, сериализуем.

mson написал: "Я не уверен, почему вы хотите сериализовать исключение..."

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

Я сделал это. Я просто создал сериализуемый класс-оболочку, который заменяет IDictionary сериализуемой альтернативой (массив KeyValuePair)

/// <summary>
/// A wrapper class for serializing exceptions.
/// </summary>
[Serializable] [DesignerCategory( "code" )] [XmlType( AnonymousType = true, Namespace = "http://something" )] [XmlRootAttribute( Namespace = "http://something", IsNullable = false )] public class SerializableException
{
    #region Members
    private KeyValuePair<object, object>[] _Data; //This is the reason this class exists. Turning an IDictionary into a serializable object
    private string _HelpLink = string.Empty;
    private SerializableException _InnerException;
    private string _Message = string.Empty;
    private string _Source = string.Empty;
    private string _StackTrace = string.Empty;
    #endregion

    #region Constructors
    public SerializableException()
    {
    }

    public SerializableException( Exception exception ) : this()
    {
        setValues( exception );
    }
    #endregion

    #region Properties
    public string HelpLink { get { return _HelpLink; } set { _HelpLink = value; } }
    public string Message { get { return _Message; } set { _Message = value; } }
    public string Source { get { return _Source; } set { _Source = value; } }
    public string StackTrace { get { return _StackTrace; } set { _StackTrace = value; } }
    public SerializableException InnerException { get { return _InnerException; } set { _InnerException = value; } } // Allow null to be returned, so serialization doesn't cascade until an out of memory exception occurs
    public KeyValuePair<object, object>[] Data { get { return _Data ?? new KeyValuePair<object, object>[0]; } set { _Data = value; } }
    #endregion

    #region Private Methods
    private void setValues( Exception exception )
    {
        if ( null != exception )
        {
            _HelpLink = exception.HelpLink ?? string.Empty;
            _Message = exception.Message ?? string.Empty;
            _Source = exception.Source ?? string.Empty;
            _StackTrace = exception.StackTrace ?? string.Empty;
            setData( exception.Data );
            _InnerException = new SerializableException( exception.InnerException );
        }
    }

    private void setData( ICollection collection )
    {
        _Data = new KeyValuePair<object, object>[0];

        if ( null != collection )
            collection.CopyTo( _Data, 0 );
    }
    #endregion
}

Если вы пытаетесь сериализовать исключение для журнала, возможно, лучше сделать a .ToString (), а затем сериализуйте это в свой журнал.

но здесь статья о том, как это сделать, и почему. В принципе, вам нужно реализовать ISerializable на вашем исключении. Если это системное исключение, я считаю, что у них реализован этот интерфейс. Если это чужое исключение, вы могли бы расширить его, чтобы реализовать интерфейс ISerializable.

на всякий случай, если кто-то еще наткнется на этот поток (он находится на первой странице Google на сегодняшний день), это очень полезный класс для сериализации Exception объект в XElement (yay, LINQ) объект:

http://seattlesoftware.wordpress.com/2008/08/22/serializing-exceptions-to-xml/

код включен для полноты:

using System;
using System.Collections;
using System.Linq;
using System.Xml.Linq;

public class ExceptionXElement : XElement
{
    public ExceptionXElement(Exception exception)
        : this(exception, false)
    { ; }


    public ExceptionXElement(Exception exception, bool omitStackTrace)
        : base(new Func<XElement>(() =>
        {
            // Validate arguments
            if (exception == null)
            {
                throw new ArgumentNullException("exception");
            }

            // The root element is the Exception's type
            XElement root = new XElement(exception.GetType().ToString());
            if (exception.Message != null)
            {
                root.Add(new XElement("Message", exception.Message));
            }

            // StackTrace can be null, e.g.:
            // new ExceptionAsXml(new Exception())
            if (!omitStackTrace && exception.StackTrace != null)
            {
                vroot.Add(
                    new XElement("StackTrace",
                    from frame in exception.StackTrace.Split('\n')
                    let prettierFrame = frame.Substring(6).Trim()
                    select new XElement("Frame", prettierFrame))
                );
            }

            // Data is never null; it's empty if there is no data
            if (exception.Data.Count > 0)
            {
                root.Add(
                    new XElement("Data",
                        from entry in exception.Data.Cast<DictionaryEntry>()
                        let key = entry.Key.ToString()
                        let value = (entry.Value == null) ? "null" : entry.Value.ToString()
                        select new XElement(key, value))
                );
            }

            // Add the InnerException if it exists
            if (exception.InnerException != null)
            {
                root.Add(new ExceptionXElement(exception.InnerException, omitStackTrace));
            }
            return root;
        })())
    { ; }
}

создать protected конструктор вроде этого (также вы должны отметить свой Exception класс [Serializable]):

protected MyException(System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context):base(info,context)
{
}

Я не уверен, почему вы хотите сериализовать исключение...

Если бы я хотел сделать то, что вы укажете, я бы создал пользовательский класс исключений, который реализует ISerializable. Вы можете выбрать, чтобы сделать его дочерним исключением, или у вас может быть полностью пользовательский класс, который имеет и делает только то, что вам нужно.

Это старая тема, но достойная другого ответа.

@mson задавался вопросом, почему кто-то хочет сериализовать исключение. Вот наша причина для этого:

у нас есть приложение Prism/MVVM с представлениями как в Silverlight, так и в WPF, с моделью данных в службах WCF. Мы хотим быть уверены, что доступ к данным и обновления происходят без ошибок. Если есть ошибка, мы хотим знать об этом немедленно, и пусть пользователь знает, что что-то, возможно, не удалось. Наш приложения откроют окно, информирующее пользователя о возможной ошибке. Фактическое исключение затем отправляется нам по электронной почте и хранится в SpiceWorks для отслеживания. Если ошибка возникает на службе WCF, мы хотим получить полное исключение обратно клиенту, так что этот процесс может произойти.

вот решение, которое я придумал, которое может быть обработано как клиентами WPF, так и Silverlight. Методы ниже a в" общей " библиотеке классов методов, используемых несколькими приложениями в каждом слой.

массив байтов легко сериализуется из службы WCF. Практически любой объект может быть преобразован в массив байтов.

Я начал с двух простых методов, Object2Bytes и Bytes2Object. Они преобразуют любой объект в массив байтов и обратно. NetDataContractSerializer-это версия системы Windows.Во время выполнения.Пространство Имен Сериализации.

Public Function Object2Bytes(ByVal value As Object) As Byte()
    Dim bytes As Byte()
    Using ms As New MemoryStream
        Dim ndcs As New NetDataContractSerializer()
        ndcs.Serialize(ms, value)
        bytes = ms.ToArray
    End Using
    Return bytes
End Function

Public Function Bytes2Object(ByVal bytes As Byte()) As Object
    Using ms As New MemoryStream(bytes)
        Dim ndcs As New NetDataContractSerializer
        Return ndcs.Deserialize(ms)
    End Using
End Function

Первоначально мы возвращали бы все результаты как объект. Если объект, возвращающийся из службы, был массивом байтов, тогда мы знали, что это исключение. Затем мы вызовем "Bytes2Object" и бросим исключение для обработки.

проблема с этим кодом, он несовместим с Silverlight. Поэтому для наших новых приложений я сохранил старые методы для трудно сериализуемых объектов и создал пару новых методов только для исключений. DataContractSerializer также из системы.Во время выполнения.Пространство имен сериализации, но оно присутствует как в Windows, так и Версий Silverlight.

Public Function ExceptionToByteArray(obj As Object) As Byte()
    If obj Is Nothing Then Return Nothing
    Using ms As New MemoryStream
        Dim dcs As New DataContractSerializer(GetType(Exception))
        dcs.WriteObject(ms, obj)
        Return ms.ToArray
    End Using
End Function

Public Function ByteArrayToException(bytes As Byte()) As Exception
    If bytes Is Nothing OrElse bytes.Length = 0 Then
        Return Nothing
    End If
    Using ms As New MemoryStream
        Dim dcs As New DataContractSerializer(GetType(Exception))
        ms.Write(bytes, 0, bytes.Length)
        Return CType(dcs.ReadObject(ms), Exception)
    End Using
End Function

при отсутствии ошибок служба WCF возвращает значение 1. Если возникает ошибка, он передает исключение методу, который вызывает "ExceptionToByteArray", а затем генерирует уникальное целое число из текущего времени. Он использует это число в качестве ключа для кэширования массива байтов в течение 60 секунд. Затем служба WCF возвращает клиенту значение ключа.

когда клиент видит, что он получил обратно целое число, отличное от 1, он делает вызов службы Метод "GetException", использующий это значение ключа. Служба извлекает массив байтов из кэша и отправляет его обратно клиенту. Клиент вызывает "ByteArrayToException" и обрабатывает исключение, как я описал выше. 60 секунд-это уйма времени для клиента, чтобы запросить исключение из службы. Менее чем через минуту кэш памяти сервера будет очищен.

Я думаю, что это проще, чем создание пользовательского класса исключения. Я надеюсь, что это поможет кому-то позже.