IT TIP

C #에서 Exception 개체를 직렬화하는 방법은 무엇입니까?

itqueen 2020. 10. 28. 21:24
반응형

C #에서 Exception 개체를 직렬화하는 방법은 무엇입니까?


C #에서 Exception 개체를 직렬화하려고합니다. 그러나 Exception 클래스가로 표시되지 않았기 때문에 불가능한 것으로 보입니다 [Serializable]. 이를 해결하는 방법이 있습니까?

응용 프로그램 실행 중에 문제가 발생하면 발생한 예외를 알려 드리고 싶습니다.

내 첫 번째 반사는 그것을 직렬화하는 것입니다.


내가 전에 한 일은 사용자 정의 Error 클래스를 만드는 것입니다. 이것은 Exception에 대한 모든 관련 정보를 캡슐화하며 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) { }
}

Exception 클래스 Serializable로 표시되고 ISerializable을 구현합니다. MSDN 참조 : http://msdn.microsoft.com/en-us/library/system.exception.aspx

를 사용하여 XML로 직렬화하려고하면 XmlSerializer을 구현하는 모든 멤버에서 오류가 발생합니다 IDictionary. 이는 XmlSerializer의 제한 사항이지만 클래스는 확실히 직렬화 가능합니다.


mson은 다음과 같이 썼습니다. "왜 예외를 직렬화 하려는지 잘 모르겠습니다 ..."

예외를 직렬화하여 웹 서비스를 통해 역 직렬화 한 다음 다시 던지거나 기록하거나 처리 할 수있는 호출 개체로 예외를 버블 링합니다.

난 이걸했다. IDictionary를 직렬화 가능한 대안 (KeyValuePair 배열)으로 대체하는 Serializable 래퍼 클래스를 간단히 만들었습니다.

/// <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(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;
        })())
    { ; }
}

Create a protected constructor like this (also you should mark your Exception class [Serializable]):

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

I'm not sure why you would want to serialize the exception...

If I did want to do what you specify, I'd create a custom Exception class that implements ISerializable. You can choose to make it child of Exception or you could have it be a completely custom class that only has and does what you need.


This is an old thread, but worthy of another answer.

@mson wondered why anyone would want to serialize an Exception. Here's our reason for doing it:

We have a Prism/MVVM application with views in both Silverlight and WPF, with the Data Model in WCF services. We want to be sure that data access and updates occur without error. If there is an error, we want to know about it immediately, and let the user know that something may have failed. Our applications will pop a window informing the user of a possible error. The actual exception is then e-mailed to us, and stored in SpiceWorks for tracking. If the error occurs on a WCF service, we want to get the full exception back to the client so this process can happen.

Here is the solution I came up with that can be handled by both WPF and Silverlight clients. The methods below a in a "Common" class library of methods used by multiple applications in every layer.

A byte array is easily serialized from a WCF service. Pretty much any object can be converted into a byte array.

I started with two simple methods, Object2Bytes and Bytes2Object. These convert any object to a Byte array and back. NetDataContractSerializer is from the Windows version of the System.Runtime.Serialization Namespace.

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

Originally, we would return all results as an Object. If the object coming back from the service was a byte array, then we knew it was an exception. Then we would call "Bytes2Object" and throw the exception for handling.

Trouble with this code is, it's incompatible with Silverlight. So for our new applications I kept the old methods for hard-to-serialize objects, and created a pair of new methods just for exceptions. DataContractSerializer is also from the System.Runtime.Serialization Namespace, but it is present in both the Windows and Silverlight versions.

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

When no errors occur, the WCF service returns 1. If an error occurs, it passes the Exception to a method that calls "ExceptionToByteArray," then generates a unique integer from the present time. It uses that integer as a key to cache the byte array for 60 seconds. The WCF service then returns the key value to the client.

When the client sees that it got back an integer other than 1, it makes a call to the Service's "GetException" method using that key value. The service fetches the byte array from cache and sends it back to the client. The client calls "ByteArrayToException" and processes the exception as I described above. 60 seconds is plenty of time for the client to request the exception from the service. In less than a minute, the server's MemoryCache is cleared.

I think this is easier than creating a custom Exception class. I hope this is a help to somebody later on.


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

//To serialize your custom exception
JsonConvert.SerializeObject(customException);

참고URL : https://stackoverflow.com/questions/486460/how-to-serialize-an-exception-object-in-c

반응형