web-dev-qa-db-ja.com

C#でExceptionオブジェクトをシリアル化する方法は?

C#で例外オブジェクトをシリアル化しようとしています。ただし、Exceptionクラスは [Serializable] 。それを回避する方法はありますか?

アプリケーションの実行中に何か問題が発生した場合、発生した例外について通知を受けたいです。

私の最初の反射は、それをシリアル化することです。

71
Martin

以前に行ったことは、カスタム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;
    }
}
39
davogones

[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) { }
}
48
David Crow

例外クラスis Serializableとしてマークされ、ISerializableを実装しています。MSDNを参照してください: http://msdn.Microsoft .com/en-us/library/system.exception.aspx

XmlSerializerを使用してXMLにシリアル化しようとすると、IDictionaryを実装するすべてのメンバーでエラーが発生します。これはXmlSerializerの制限ですが、クラスは確かにシリアル化可能です。

37
Rex M

msonは次のように書いています。「例外をシリアル化する理由がわかりません...」

例外をシリアル化して、Webサービスを介して、例外をシリアル化、再スロー、ログ、またはその他の方法で処理できる呼び出し元オブジェクトにバブルアップします。

これは私がしました。 IDictionaryをシリアル化可能な代替(KeyValuePair配列)に置き換えるSerializable wrapperクラスを作成しました

/// <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
}
17
Antony Booth

ログの例外をシリアル化しようとしている場合は、.ToString()を実行し、それをログにシリアル化することをお勧めします。

しかし、 ここにあります その方法とその理由に関する記事。基本的に、例外にISerializableを実装する必要があります。システム例外の場合、そのインターフェースが実装されていると思います。他の誰かの例外である場合、サブクラス化してISerializableインターフェイスを実装できる場合があります。

8
mmr

他の誰かがこのスレッドに出くわした場合に備えて(今日のGoogleのページ1にあります)、これはExceptionオブジェクトをXElementにシリアル化するのに非常に便利なクラスです(はい、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;
        })())
    { ; }
}
7
Tieson T.

このようなprotectedコンストラクターを作成します(また、Exception class [Serializable]):

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

例外をシリアル化する理由がわかりません...

指定したことを実行したい場合は、ISerializableを実装するカスタム例外クラスを作成します。 Exceptionの子にするか、必要なものだけを持ち、実行する完全にカスタムクラスにすることができます。

1
mson

これは古いスレッドですが、別の答えに値します。

@msonは、なぜ誰も例外をシリアル化するのか疑問に思いました。これを行う理由は次のとおりです。

SilverlightとWPFの両方のビューと、WCFサービスのデータモデルを備えたPrism/MVVMアプリケーションがあります。データアクセスと更新がエラーなしで発生することを確認する必要があります。エラーが発生した場合、すぐにそのことを知り、何かが失敗した可能性があることをユーザーに知らせます。私たちのアプリケーションは、エラーの可能性をユーザーに知らせるウィンドウをポップします。その後、実際の例外が電子メールで送信され、追跡のためにSpiceWorksに保存されます。 WCFサービスでエラーが発生した場合、このプロセスが発生するように、クライアントに完全な例外を取得する必要があります。

これが、WPFクライアントとSilverlightクライアントの両方で処理できる解決策です。以下のメソッドは、すべてのレイヤーの複数のアプリケーションによって使用されるメソッドの「共通」クラスライブラリにあります。

バイト配列はWCFサービスから簡単にシリアル化されます。ほとんどのオブジェクトはバイト配列に変換できます。

Object2BytesとBytes2Objectの2つの簡単なメソッドから始めました。これらは、任意のオブジェクトをバイト配列に変換して戻します。 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秒です。 1分以内に、サーバーのMemoryCacheがクリアされます。

これは、カスタムのExceptionクラスを作成するよりも簡単だと思います。これが後で誰かの助けになることを願っています。

1
D'Hag