【问题标题】:In C#, how can I serialize System.Exception? (.Net CF 2.0)在 C# 中,如何序列化 System.Exception? (.Net CF 2.0)
【发布时间】:2010-09-28 06:01:08
【问题描述】:

我想向 MS 消息队列写入异常。当我尝试它时,我得到了一个例外。所以我尝试通过使用仍然引发异常的 XmlSerializer 来简化它,但它给了我更多信息:

{"反映类型有错误 'System.Exception'。"}

带有内部异常:

{"无法序列化成员 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();

编辑: 我尽量保持简单,但我可能做得过火了。我想要整个位、堆栈跟踪、消息、自定义异常类型和自定义异常属性。我什至可能想再次抛出异常。

【问题讨论】:

    标签: c# exception serialization compact-framework


    【解决方案1】:

    我正在查看 Jason Jackson 的答案,但即使 System.Exception 实现了 ISerializable,我也没有任何意义。因此,我通过将异常包装在使用 BinaryFormatter 的类中来绕过 XmlSerializer。当 MS 消息队列对象的 XmlSerialization 启动时,它将看到一个具有公共字节数组的类。

    这是我想出的:

    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 上实现。

    编辑:上面的代码 sn-ps 在桌面项目中。在 CF 版本中,WrappedException 基本上看起来与我只是需要实现我自己的 BinaryFormater 相同,但我非常愿意接受关于那个的建议。

    【讨论】:

      【解决方案2】:

      评论:

      在跨进程边界进行远程处理或与系统交互时,序列化异常是一项常见任务。不要听别人说别的;他们可能从未编写过远程处理库。

      解决方案:

      我之前通过创建一个自定义的基本异常类来进行远程处理。我遇到的问题是 System.Exception 不容易序列化,所以我必须继承它。我处理这个问题的方法是创建我自己的序列化异常(通过 ISerializable),并将任何 System.Exception 包装在自定义异常中。

      在整个服务器代码中,无论如何您都应该使用自定义异常,这些都可以基于您的可序列化基类型。工作量不大,你很快就会建立一个通用的异常库来通过。

      您写入队列(并从中读取)的层应该执行所有异常序列化/水合。您可能会考虑这样的事情:

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

      与您的队列对话的服务器层和客户端层会将您发送的对象包装在 Payload 中,或者附加一个异常(如果有)。当从队列中消耗数据时,客户端层可以检查异常并重新抛出异常,否则将数据交给您。

      这是我之前写的和我看到其他人写的非常简单的版本。祝你的项目好运。

      【讨论】:

        【解决方案3】:

        我认为你基本上有两种选择:

        1. 进行您自己的手动序列化(可能不想这样做)。由于您在内部异常中得到的确切消息,XML 序列化肯定不会起作用。
        2. 创建您自己的自定义(可序列化)异常类,将抛出的异常中的数据注入您的自定义异常类并对其进行序列化。

        【讨论】:

          【解决方案4】:

          为什么?您是否在从消息队列中检索异常时实例化它?如果没有,只需发送异常消息(作为字符串)...

          【讨论】:

          • 或者更好的是,将 StackTrace 属性作为字符串发送
          • >您是否在从消息队列中检索异常时实例化它?是的。
          【解决方案5】:

          嗯,XML 序列化的用途有限。例如,它不能序列化这个特定的场景。对于异常的精确序列化和反序列化,您必须使用BinaryFormatter,只要您自己的异常标记为[Serializable],它就可以工作。所有 .Net 异常都用这个SerializableAttribute 标记,这意味着它们可以用BinaryFormatter 序列化。

          不过,你必须注意一个问题。
          如果您的异常不可序列化,它显然会失败。但是,当您的异常包含不可序列化的字段时,它也会失败。您应该注意确保在您的自定义异常中这是不可能的。

          【讨论】:

          • 如果 .NET 异常被标记为 [Serializable],为什么不使用 TextFormatter 进行序列化?或者至少,当你在调试 web 服务时,为什么框架将它们扔到浏览器中时如何处理它们?
          • 我不知道TextFormatter是什么,所以我真的没有资格回答这个问题。然而,据我所知,SerializableAttribute 仅用于二进制序列化。此外,如果异常具有对象字段,并且该字段包含不可序列化的数据,则序列化将失败。
          • [...] 那么框架在调试 Web 服务时会做什么?
          【解决方案6】:

          我遵循了link 中建议的方法(向下滚动查看名为 Kubilay 的人发布的答案),它对我很有用。无需为异常对象创建任何包装类。

          【讨论】:

          • 一年后我发现自己的答案再次有用。
          • 使用NetDataContractSerializer ?
          【解决方案7】:

          您不能将字典序列化为 xml。照别人说的做,发信息,反正这很重要。

          【讨论】:

            猜你喜欢
            • 2010-09-23
            • 2012-02-05
            • 1970-01-01
            • 2014-06-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多