В C #, как я могу сериализовать System.Exception? (.Net CF 2.0) - PullRequest
18 голосов
/ 23 декабря 2008

Я хочу записать исключение в очередь сообщений MS. Когда я пытаюсь это сделать, я получаю исключение. Поэтому я попытался упростить его, используя XmlSerializer, который все еще вызывает исключение, но он дал мне немного больше информации:

{"Произошла ошибка, отражающая тип 'System.Exception'. "}

с InnerException:

{"Невозможно сериализовать элемент System.Exception.Data типа System.Collections.IDictionary, потому что он реализует IDictionary. "}

Пример кода:

        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: Я пытался сделать это как можно более простым, но, возможно, я перестарался. Я хочу, чтобы весь бит, трассировка стека, сообщение, пользовательский тип исключения и пользовательские свойства исключения. Я могу даже захотеть снова сгенерировать исключение.

Ответы [ 7 ]

10 голосов
/ 24 декабря 2008

Я смотрел на ответ Джейсона Джексона, но для меня не имело смысла, что у меня возникают проблемы с этим, хотя System.Exception реализует ISerializable. Поэтому я обошел XmlSerializer, поместив исключение в класс, который вместо этого использует BinaryFormatter. Когда XmlSerialization объектов MS Message Queuing начинает работать, он видит класс с открытым байтовым массивом.

Вот что я придумал:

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

Первый тест работал отлично, но я все еще беспокоился о пользовательских исключениях. Поэтому я собрал собственное исключение. Тогда я просто опустил кнопку на пустой форме. Вот код:

[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;

    }

Хотя казалось, что все другие типы исключений, которые я просматривал, помечены с помощью SerializableAttribute, мне нужно быть осторожным с пользовательскими исключениями, которые не помечены с помощью SerializableAttribute.

РЕДАКТИРОВАТЬ: забегая вперед. Я не понял, что BinaryFormatter не реализован на CF.

РЕДАКТИРОВАТЬ: Выше фрагменты кода были в настольном проекте. В версии CF WrappedException будет в основном выглядеть так же, как я , просто , необходим для реализации моего собственного BinaryFormater, но я очень открыт для предложений по этому вопросу.

7 голосов
/ 24 декабря 2008

Комментарий:

Сериализация исключений является распространенной задачей при удаленном взаимодействии или взаимодействии с системами через границы процессов. Не слушайте никого, кто говорит иначе; они, вероятно, никогда не писали библиотеку удаленного взаимодействия.

Решение:

Я уже подключился к удаленному взаимодействию, чтобы сделать это раньше, создав собственный базовый класс исключений. Проблема, с которой я столкнулся, состояла в том, что System.Exception не сериализуется легко, поэтому мне пришлось наследовать от него. Я справился с этим, создав собственные исключения, которые сериализовались (через ISerializable), и обернули любое исключение System.Exception в пользовательское исключение.

Во всем коде вашего сервера вы все равно должны использовать пользовательские исключения, и все они могут основываться на вашем сериализуемом базовом типе. Это не так много работы, и вы быстро создадите общую библиотеку исключений для сквозного.

Слой, который вы записываете в очередь (и считываете из него), должен выполнять всю сериализацию / гидратацию исключений. Вы можете рассмотреть что-то вроде этого:

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

Серверный и клиентский уровни, которые общаются с вашей очередью, обернут объект, который вы отправляете в полезную нагрузку, или прикрепите исключение (если есть). Когда данные используются из очереди, клиентский уровень может проверить наличие исключения и повторно выдать его, если он есть, в противном случае передать вам ваши данные.

Это очень простая версия того, что я написал раньше, и того, что, как я видел, пишут другие. Удачи в вашем проекте.

7 голосов
/ 23 декабря 2008

Я думаю, у вас есть два варианта:

  1. Выполняйте собственную сериализацию вручную (вероятно, НЕ хотите этого делать). XML-сериализация, безусловно, не будет работать из-за точного сообщения, которое вы получаете во внутреннем исключении.
  2. Создайте свой собственный класс исключений (сериализуемый), вставьте данные из брошенного исключения в свой пользовательский и сериализуйте его.
2 голосов
/ 23 декабря 2008

Почему? Вы создаете исключение после извлечения его из очереди сообщений? Если нет, просто отправьте сообщение об исключении (в виде строки) ...

0 голосов
/ 17 июня 2011

Я следовал методу, предложенному в этой ссылке (прокрутите вниз, чтобы найти ответ от человека по имени Кубилай), и он отлично работает для меня. Нет необходимости создавать какие-либо классы-оболочки для объекта исключения.

0 голосов
/ 24 декабря 2008

Ну, XML-сериализация ограничена в своем использовании. Он не может, например, сериализовать этот конкретный сценарий. Для точной сериализации и десериализации Исключений вы должны будете использовать BinaryFormatter, который будет работать, пока ваши собственные исключения помечены [Serializable]. Все исключения .Net отмечены этим SerializableAttribute, что означает, что они могут быть сериализованы с помощью BinaryFormatter.

Там есть кое-что, на что стоит обратить внимание.
Если ваше исключение не сериализуемо, оно, очевидно, потерпит неудачу. Но также произойдет сбой, если ваше исключение содержит поле, которое нельзя сериализовать. Вам следует остерегаться, чтобы убедиться, что это невозможно в ваших пользовательских исключениях.

0 голосов
/ 23 декабря 2008

Нельзя сериализовать словарь в xml. Сделайте то, что сказал другой парень, и отправьте сообщение, это важный бит в любом случае.

...