Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C#, how can I serialize System.Exception? (.Net CF 2.0)

I want to write an Exception to an MS Message Queue. When I attempt it I get an exception. So I tried simplifying it by using the XmlSerializer which still raises an exception, but it gave me a bit more info:

{"There was an error reflecting type 'System.Exception'."}

with InnerException:

{"Cannot serialize member System.Exception.Data of type System.Collections.IDictionary, because it implements IDictionary."}

Sample Code:

        Exception e = new Exception("Hello, world!");
        MemoryStream stream = new MemoryStream();
        XmlSerializer x = new XmlSerializer(e.GetType()); // Exception raised on this line

        x.Serialize(stream, e);
        stream.Close();

EDIT: I tried to keep this a simple as possible, but I may have overdone it. I want the whole bit, stack trace, message, custom exception type, and custom exception properties. I may even want to throw the exception again.

like image 819
CrashCodes Avatar asked Dec 23 '08 20:12

CrashCodes


People also ask

What is '~' in C programming?

In mathematics, the tilde often represents approximation, especially when used in duplicate, and is sometimes called the "equivalency sign." In regular expressions, the tilde is used as an operator in pattern matching, and in C programming, it is used as a bitwise operator representing a unary negation (i.e., "bitwise ...

What is operators in C?

Advertisements. An operator is a symbol that tells the compiler to perform specific mathematical or logical functions. C language is rich in built-in operators and provides the following types of operators − Arithmetic Operators.

What is the use of in C?

In C/C++, the # sign marks preprocessor directives. If you're not familiar with the preprocessor, it works as part of the compilation process, handling includes, macros, and more.

What is an expression in C?

An expression in C is a combination of operands and operators – it computes a single value stored in a variable. The operator denotes the action or operation to be performed. The operands are the items to which we apply the operation. Expression.


2 Answers

I was looking at Jason Jackson's answer, but it didn't make sense to me that I'm having problems with this even though System.Exception implements ISerializable. So I bypassed the XmlSerializer by wrapping the exception in a class that uses a BinaryFormatter instead. When the XmlSerialization of the MS Message Queuing objects kicks in all it will see is a class with a public byte array.

Here's what I came up with:

public class WrappedException {
    public byte[] Data;

    public WrappedException() {
    }

    public WrappedException(Exception e) {
        SetException(e);
    }

    public Exception GetException() {
        Exception result;
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream stream = new MemoryStream(Data);
        result = (Exception)bf.Deserialize(stream);
        stream.Close();
        return result;
    }

    public void SetException(Exception e) {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(stream, e);
        Data = stream.ToArray();
        stream.Close();
    }
}

The first test worked perfectly, but I was still concerned about custom exceptions. So I tossed together my own custom exception. Then I just dropped a button on a blank form. Here's the code:

[Serializable]

public class MyException : Exception, ISerializable {
    public int ErrorCode = 10;
    public MyException(SerializationInfo info, StreamingContext context)
        : base(info, context) {

        ErrorCode = info.GetInt32("ErrorCode");
    }

    public MyException(string message)
        : base(message) {
    }

    #region ISerializable Members
    void ISerializable.GetObjectData(SerializationInfo info, 
        StreamingContext context) {

        base.GetObjectData(info, context);
        info.AddValue("ErrorCode", ErrorCode);
    }

    #endregion
}

private void button1_Click(object sender, EventArgs e) {
    MyException ex = new MyException("Hello, world!");
    ex.ErrorCode = 20;
    WrappedException reply = new WrappedException(ex);
    XmlSerializer x = new XmlSerializer(reply.GetType());
    MemoryStream stream = new MemoryStream();
    x.Serialize(stream, reply);
    stream.Position = 0;
    WrappedException reply2 = (WrappedException)x.Deserialize(stream);
    MyException ex2 = (MyException)reply2.GetException();
    stream.Close();
    Text = ex2.ErrorCode.ToString(); // form shows 20

    // throw ex2;

    }

Although it seemed like all of other exception types that I looked up are marked with the SerializableAttribute, I'm going to have to be careful about custom exceptions that are not marked with the SerializableAttribute.

EDIT: Getting ahead of myself. I didn't realize that BinaryFormatter is not implemented on CF.

EDIT: Above code snippets were in a desktop project. In the CF version, the WrappedException will basically look the same I just need to implement my own BinaryFormater, but I'm very open to suggestions on that one.

like image 107
CrashCodes Avatar answered Sep 24 '22 15:09

CrashCodes


Commentary:

Serializing exceptions is a common task when remoting or interacting with systems across process boundaries. Don't listen to anyone who says otherwise; they have probably never written a remoting library.

Solution:

I have plumbed remoting to do this before by creating a custom, base exception class. The problem I ran into was that System.Exception does not serialize easily so I had to inherit from it. The way I handled this was by creating my own exceptions that did serialize (through ISerializable), and wrapped any System.Exception in a custom exception.

Throughout your server code you should use custom exceptions anyway, and these can all be based on your serializable base type. It is not much work, and you will quickly build up a common library of exceptions to through.

The layer you write out to the queue (and read from) should do all the exception serialization/hydration. You might consider something like this:

public class WireObject<T, E>
{
  public T Payload{get;set;}
  public E Exception{get;set;}
}

The server and client layers that talk to your queue will wrap the object you are sending in the Payload, or attach an exception (if any). When the data is consumed from the queue, the client layer can check for an exception and re-throw it if present, else hand you your data.

This is a very simple version of what I have written before, and what I have seen others write. Good luck on your project.

like image 45
Jason Jackson Avatar answered Sep 22 '22 15:09

Jason Jackson