C#で例外オブジェクトをシリアル化しようとしています。ただし、Exceptionクラスは [Serializable]
。それを回避する方法はありますか?
アプリケーションの実行中に何か問題が発生した場合、発生した例外について通知を受けたいです。
私の最初の反射は、それをシリアル化することです。
以前に行ったことは、カスタム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;
}
}
[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) { }
}
例外クラスis Serializableとしてマークされ、ISerializableを実装しています。MSDNを参照してください: http://msdn.Microsoft .com/en-us/library/system.exception.aspx
XmlSerializer
を使用してXMLにシリアル化しようとすると、IDictionary
を実装するすべてのメンバーでエラーが発生します。これはXmlSerializerの制限ですが、クラスは確かにシリアル化可能です。
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
}
ログの例外をシリアル化しようとしている場合は、.ToString()を実行し、それをログにシリアル化することをお勧めします。
しかし、 ここにあります その方法とその理由に関する記事。基本的に、例外にISerializableを実装する必要があります。システム例外の場合、そのインターフェースが実装されていると思います。他の誰かの例外である場合、サブクラス化してISerializableインターフェイスを実装できる場合があります。
他の誰かがこのスレッドに出くわした場合に備えて(今日の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;
})())
{ ; }
}
このようなprotected
コンストラクターを作成します(また、Exception
class [Serializable]
):
protected MyException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context):base(info,context)
{
}
例外をシリアル化する理由がわかりません...
指定したことを実行したい場合は、ISerializableを実装するカスタム例外クラスを作成します。 Exceptionの子にするか、必要なものだけを持ち、実行する完全にカスタムクラスにすることができます。
これは古いスレッドですが、別の答えに値します。
@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クラスを作成するよりも簡単だと思います。これが後で誰かの助けになることを願っています。