Як серіалізувати об'єкт винятку в C #?


74

Я намагаюся серіалізувати об'єкт Exception у C #. Однак, схоже, це неможливо, оскільки клас Exception не позначений як [Serializable]. Чи є спосіб обійти це?

Якщо під час виконання програми щось піде не так, я хочу бути проінформованим за винятком, який стався.

Мій перший рефлекс - це серіалізація.




System.ExceptionКлас відзначений як Серіалізуемое msdn.microsoft.com/en-us/library / ...
Jodrell

Відповіді:


41

Я робив раніше, це створити власний клас Error. Це інкапсулює всю відповідну інформацію про виняток і піддається серіалізації 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;
    }
}

1
Див. "Проектування спеціальних винятків" msdn.microsoft.com/en-us/library/ms229064(v=VS.100).aspx

5
Проблема цього полягає в тому, що якщо існує внутрішній виняток, який насправді містить деталі, ви цього не включатимете.
greektreat

51

Створіть власний клас винятків за допомогою атрибута [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) { }
}

Це розібрало мене з проблемою з винятками та віддаленням.
0E322070,

1
А як щодо вмісту повідомлення? Чи повинен конструктор серіалізації насправді щось робити, щоб витягнути повідомлення з потокового контексту чи щось інше?
Лукас,

Зверніть увагу, що якщо ви використовуєте це з внутрішнім винятком, який не позначений як серіалізується, ви отримаєте помилку під час виконання під час серіалізації. Не проблема, якщо внутрішній виняток із .NET framework, але є точкою відмови, якщо ви використовуєте власний клас винятків і забули атрибут Serializable.
rybo103

2
System.Net.Sockets.SocketExceptionа System.Net.Http.HttpRequestException НЕ СЕРІАЛІЗУЄТЬСЯ ?
Кікенет,

39

Клас Exception є позначений як Serializable та інвентар ISerializable. Див. MSDN: http://msdn.microsoft.com/en-us/library/system.exception.aspx

Якщо ви намагаєтеся виконати серіалізацію до XML за допомогою XmlSerializer, ви отримаєте повідомлення про помилку на всіх учасниках, які реалізують IDictionary. Це обмеження XmlSerializer, але клас, безумовно, можна серіалізувати.


Ще одне обмеження: якщо ви використовуєте Silverlight і WCF, об’єкти, передані вперед і назад, не можуть бути ISerializable для їх серіалізації. Я намагався передати об'єкт Exception, але він не спрацював, оскільки ISerializable недоступний у Silverlight.
Джон Гілмер

17

mson пише: "Я не впевнений, чому ви хочете серіалізувати виняток ..."

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

Я зробив це. Я просто створив клас обгортки Serializable, який замінює 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
}

3
Ctrl + K, Ctrl + D, тоді ви можете припинити скаржитися на перенесення рядків.
Ентоні Бут,

4
@AdamNaylor Перегляд (наприклад) властивостей. Я вважаю найзручнішим лише розглянути 6 рядків коду, щоб побачити всі властивості, замість (принаймні, 6x11 =) 66 рядків коду. Ви просто (так само, як я зараз) висловлюєтесь taste. Отже, Blurgh!більше говориться про те, як ви висловлюєте себе, ніж про правильне форматування коду. Але, мабуть, ви це вже знали ...
Майк де Клерк

VS, природно, намагатиметься протистояти довгим рядкам коду. Смак це не враховує.
Адам Нейлор,

Я щойно запропонував заміну, тому ваш код підтримуватиме серіалізацію деяких несеріалізованих винятків, таких як SqlException. Велике спасибі, що поділились своїм кодом.
Хуліо Нобре

З цікавості, що трапиться, якщо Dataмістить значення, яке не можна серіалізувати?
канон

9

Якщо ви намагаєтесь серіалізувати виняток для журналу, можливо, було б краще зробити файл .ToString (), а потім серіалізувати його до вашого журналу.

Але ось стаття про те, як це зробити, і чому. По суті, вам потрібно реалізувати ISerializable на вашому винятку. Якщо це системний виняток, я вважаю, що цей інтерфейс реалізований. Якщо це виняток когось іншого, можливо, ви зможете підкласувати його для реалізації інтерфейсу ISerializable.


4
Тому зараз, майже через два роки, я отримую голос проти. Хочете пояснити, голос проти?
mmr

6
Може, тому, що посилання на статтю порушено?
thepirat000

8

Ось дуже корисний клас для серіалізації 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;
        })())
    { ; }
}

5

Створіть такий protectedконструктор (також ви повинні позначити свій Exceptionклас [Serializable]):

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

0

Я не впевнений, чому ви хочете серіалізувати виняток ...

Якби я хотів зробити те, що ви вказали, я б створив власний клас Exception, який реалізує ISerializable. Ви можете зробити його підпорядкованим винятковій ситуації, або ж це може бути повністю власний клас, який має і робить лише те, що вам потрібно.


0

Це стара тема, але варта іншої відповіді.

@mson дивувався, чому хтось хотів би серіалізувати виняток. Ось наша причина для цього:

У нас є програма Prism / MVVM з поданнями як в Silverlight, так і в WPF, з моделлю даних у службах WCF. Ми хочемо бути впевненими, що доступ до даних та оновлення відбуваються без помилок. Якщо є помилка, ми хочемо негайно дізнатися про неї та повідомити користувача, що щось могло не вдатися. Наші програми відкриють вікно, повідомляючи користувачеві про можливу помилку. Потім фактичний виняток надсилається нам електронною поштою та зберігається у SpiceWorks для відстеження. Якщо помилка виникає в службі WCF, ми хочемо повернути повний виняток назад клієнту, щоб цей процес міг статися.

Ось рішення, яке я придумав, з яким можуть впоратись як клієнти WPF, так і Silverlight. Методи нижче a у "Загальній" бібліотеці класів методів, що використовуються багатьма програмами на кожному рівні.

Байтовий масив легко серіалізується із служби WCF. Практично будь-який об'єкт можна перетворити в байтовий масив.

Я почав з двох простих методів, Object2Bytes та Bytes2Object. Вони перетворюють будь-який об’єкт у байтовий масив і назад. NetDataContractSerializer - це версія Windows простору імен System.Runtime.Serialization.

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 також походить із простору імен System.Runtime.Serialization, але він присутній як у версіях для 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 секунд - це достатньо часу, щоб клієнт вимагав виняток із послуги. Менш ніж за хвилину очищується MemoryCache сервера.

Я думаю, що це простіше, ніж створити власний клас Exception. Я сподіваюся, це пізніше допоможе комусь.


1
Проблема такої реалізації полягає в тому, що клієнт не обов'язково має ті самі збірки, що завантажені як сервер, і при спробі десеріалізувати внутрішній виняток може бути вилучено SerializationException.
Крістоф

-2
[Serializable]
public class CustomException: Exception
{
    public CustomException: ( Exception exception ) : base(exception.Message)
    {             
        Data.Add("StackTrace",exception.StackTrace);
    }      
}

Щоб серіалізувати власний виняток:

JsonConvert.SerializeObject(customException);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.