什么是正确的方法,使一个自定义的.NET异常序列化? [英] What is the correct way to make a custom .NET Exception serializable?

查看:251
本文介绍了什么是正确的方法,使一个自定义的.NET异常序列化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更具体地,当异常包含其可以或可以不本身可序列化。定制对象

拿这个例子:

 公共类MyException:异常
{
    私人只读字符串资源名称;
    私人只读的IList<字符串> validationErrors;

    公共MyException(字符串资源名称,IList的<字符串> validationErrors)
    {
        this.resourceName =资源名称;
        this.validationErrors = validationErrors;
    }

    公共字符串资源名称
    {
        {返回this.resourceName; }
    }

    公众的IList<字符串> ValidationErrors
    {
        {返回this.validationErrors; }
    }
}
 

如果该异常序列化和反序列化,这两个自定义属性(<$ C C $>资源名称和 ValidationErrors )不会是preserved。该属性将返回

是否有实现序列化自定义异常的共同code模式?

解决方案

基本实现无自定义属性

SerializableExceptionWithoutCustomProperties.cs:

 命名空间SerializableExceptions
{
    使用系统;
    使用System.Runtime.Serialization;

    [可序列化]
    //重要说明:此属性是不是从Exception继承的,必须指定
    //否则序列化将失败,并SerializationException说明
    //,在大会Y型X没有标记为可序列化。
    公共类SerializableExceptionWithoutCustomProperties:异常
    {
        公共SerializableExceptionWithoutCustomProperties()
        {
        }

        公共SerializableExceptionWithoutCustomProperties(字符串消息)
            :碱(消息)
        {
        }

        公共SerializableExceptionWithoutCustomProperties(字符串消息,异常的InnerException)
            :基地(消息的InnerException)
        {
        }

        //如果没有这个构造函数,反序列化将失败
        保护SerializableExceptionWithoutCustomProperties(SerializationInfo中的信息,的StreamingContext上下文)
            :基地(信息,上下文)
        {
        }
    }
}
 

全面实施,具有自定义属性

完全实现自定义序列化异常( MySerializableException ),并派生密封异常( MyDerivedSerializableException )。

这个实施要点总结如下:

  1. 您的必须装点每个派生类与 [Serializable接口] 属性 - 此属性不是从基类继承,如果没有指定,序列化将失败,一个 SerializationException 指出的,在大会Y型X没有标记为可序列化。
  2. 您的必须实现自定义序列化。该 [Serializable接口] 属性是不够的 - 异常工具 ISerializable的这意味着你的派生类还必须实现自定义序列化。这包括两个步骤:
    1. 提供一个序列化构造。此构造应私人如果你的类是密封,否则应保护允许访问派生类。
    2. 覆盖GetObjectData使用()并确保你通过打电话到 base.GetObjectData(信息,上下文)末,为了让基础类保存自己的状态。

SerializableExceptionWithCustomProperties.cs:

 命名空间SerializableExceptions
{
    使用系统;
    使用System.Collections.Generic;
    使用System.Runtime.Serialization;
    使用System.Security.Permissions;

    [可序列化]
    //重要说明:此属性是不是从Exception继承的,必须指定
    //否则序列化将失败,并SerializationException说明
    //,在大会Y型X没有标记为可序列化。
    公共类SerializableExceptionWithCustomProperties:异常
    {
        私人只读字符串资源名称;
        私人只读的IList&LT;字符串&GT; validationErrors;

        公共SerializableExceptionWithCustomProperties()
        {
        }

        公共SerializableExceptionWithCustomProperties(字符串消息)
            :碱(消息)
        {
        }

        公共SerializableExceptionWithCustomProperties(字符串消息,异常的InnerException)
            :基地(消息的InnerException)
        {
        }

        公共SerializableExceptionWithCustomProperties(字符串消息,串资源名称,IList的&LT;字符串&GT; validationErrors)
            :碱(消息)
        {
            this.resourceName =资源名称;
            this.validationErrors = validationErrors;
        }

        公共SerializableExceptionWithCustomProperties(字符串消息,串资源名称,IList的&LT;字符串&GT; validationErrors,异常的InnerException)
            :基地(消息的InnerException)
        {
            this.resourceName =资源名称;
            this.validationErrors = validationErrors;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter =真)
        //构造应该被保护的非密封类,私人的密封类。
        //(串行器调用此构造函数通过反射,因此它可以是私有的)
        保护SerializableExceptionWithCustomProperties(SerializationInfo中的信息,的StreamingContext上下文)
            :基地(信息,上下文)
        {
            this.resourceName = info.GetString(资源名称);
            this.validationErrors =(IList的&LT;字符串&GT;)info.GetValue(ValidationErrors的typeof(IList的&LT;字符串&GT;));
        }

        公共字符串资源名称
        {
            {返回this.resourceName; }
        }

        公众的IList&LT;字符串&GT; ValidationErrors
        {
            {返回this.validationErrors; }
        }

        [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter =真)
        公众覆盖无效GetObjectData使用(SerializationInfo中的信息,的StreamingContext上下文)
        {
            如果(资讯== NULL)
            {
                抛出新ArgumentNullException(信息);
            }

            info.AddValue(资源名称,this.ResourceName);

            //注意:如果名单,其中,T&gt;中是不是序列化的,你可能需要解决的另一个
            //加入列表的方法,这是作秀?
            info.AddValue(ValidationErrors,this.ValidationErrors的typeof(IList的&LT;字符串&GT;));

            //必须调用通过对基类让它保存自己的状态
            base.GetObjectData(信息,上下文);
        }
    }
}
 

DerivedSerializableExceptionWithAdditionalCustomProperties.cs:

 命名空间SerializableExceptions
{
    使用系统;
    使用System.Collections.Generic;
    使用System.Runtime.Serialization;
    使用System.Security.Permissions;

    [可序列化]
    公共密封类DerivedSerializableExceptionWithAdditionalCustomProperty:SerializableExceptionWithCustomProperties
    {
        私人只读字符串用户名;

        公共DerivedSerializableExceptionWithAdditionalCustomProperty()
        {
        }

        公共DerivedSerializableExceptionWithAdditionalCustomProperty(字符串消息)
            :碱(消息)
        {
        }

        公共DerivedSerializableExceptionWithAdditionalCustomProperty(字符串消息,异常的InnerException)
            :基地(消息的InnerException)
        {
        }

        公共DerivedSerializableExceptionWithAdditionalCustomProperty(字符串消息,用户名字符串,字符串资源名称,IList的&LT;字符串&GT; validationErrors)
            :基地(信息,资源名称,validationErrors)
        {
            this.username =用户名;
        }

        公共DerivedSerializableExceptionWithAdditionalCustomProperty(字符串消息,用户名字符串,字符串资源名称,IList的&LT;字符串&GT; validationErrors,异常的InnerException)
            :基地(信息,资源名称,validationErrors,的InnerException)
        {
            this.username =用户名;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter =真)
        //序列化的构造函数是私有的,因为这个类是密封
        私人DerivedSerializableExceptionWithAdditionalCustomProperty(SerializationInfo中的信息,的StreamingContext上下文)
            :基地(信息,上下文)
        {
            this.username = info.GetString(用户名);
        }

        公共字符串用户名
        {
            {返回this.username; }
        }

        公众覆盖无效GetObjectData使用(SerializationInfo中的信息,的StreamingContext上下文)
        {
            如果(资讯== NULL)
            {
                抛出新ArgumentNullException(信息);
            }
            info.AddValue(用户名,this.username);
            base.GetObjectData(信息,上下文);
        }
    }
}
 


单元测试

MSTest的单元测试三个异常类型上述定义。

UnitTests.cs:

 命名空间SerializableExceptions
{
    使用系统;
    使用System.Collections.Generic;
    使用System.IO;
    使用System.Runtime.Serialization.Formatters.Binary;
    使用Microsoft.VisualStudio.TestTools.UnitTesting;

    [识别TestClass]
    公共类单元测试
    {
        私人常量字符串消息=小部件已经不可避免blooped了。
        私人常量字符串资源名称=资源A;
        私人常量字符串ValidationError1 =你忘了设置高手帮的标志。
        私人常量字符串ValidationError2 =沃利无法在零重力操作。
        私人只读表&LT;字符串&GT; validationErrors =新的名单,其中,串&GT;();
        私人常量字符串用户名=巴里;

        公共单元测试()
        {
            validationErrors.Add(ValidationError1);
            validationErrors.Add(ValidationError2);
        }

        [测试方法]
        公共无效TestSerializableExceptionWithoutCustomProperties()
        {
            例外EX =
                新SerializableExceptionWithoutCustomProperties(
                    消息,新的异常(内部异常。));

            //保存完整的ToString()值,其中包括异常消息和堆栈跟踪。
            字符串exceptionToString = ex.ToString();

            //往返异常:序列化并用的BinaryFormatter反序列化
            BinaryFormatter的BF =新的BinaryFormatter();
            使用(MemoryStream的毫秒=新的MemoryStream())
            {
                //保存对象状态
                bf.Serialize(MS,EX);

                //重新使用相同的流为反序列
                ms.Seek(0,0);

                //替换为反序列化的一个原始异常
                离=(SerializableExceptionWithoutCustomProperties)bf.Deserialize(毫秒);
            }

            //仔细检查异常消息和堆栈跟踪(由基异常独资)是preserved
            Assert.AreEqual(exceptionToString,ex.ToString(),ex.ToString());
        }

        [测试方法]
        公共无效TestSerializableExceptionWithCustomProperties()
        {
            SerializableExceptionWithCustomProperties EX =
                新SerializableExceptionWithCustomProperties(信息,资源名称,validationErrors);

            //完整性检查:确保自定义属性序列化之前设置
            Assert.AreEqual(消息,ex.Message,消息);
            Assert.AreEqual(资源名称,ex.ResourceName,ex.ResourceName);
            Assert.AreEqual(2,ex.ValidationErrors.Count,ex.ValidationErrors.Count);
            Assert.AreEqual(ValidationError1,ex.ValidationErrors [0],ex.ValidationErrors [0]);
            Assert.AreEqual(ValidationError2,ex.ValidationErrors [1],ex.ValidationErrors [1]);

            //保存完整的ToString()值,其中包括异常消息和堆栈跟踪。
            字符串exceptionToString = ex.ToString();

            //往返异常:序列化并用的BinaryFormatter反序列化
            BinaryFormatter的BF =新的BinaryFormatter();
            使用(MemoryStream的毫秒=新的MemoryStream())
            {
                //保存对象状态
                bf.Serialize(MS,EX);

                //重新使用相同的流为反序列
                ms.Seek(0,0);

                //替换为反序列化的一个原始异常
                离=(SerializableExceptionWithCustomProperties)bf.Deserialize(毫秒);
            }

            //确保自定义属性序列化之后pserved $ P $
            Assert.AreEqual(消息,ex.Message,消息);
            Assert.AreEqual(资源名称,ex.ResourceName,ex.ResourceName);
            Assert.AreEqual(2,ex.ValidationErrors.Count,ex.ValidationErrors.Count);
            Assert.AreEqual(ValidationError1,ex.ValidationErrors [0],ex.ValidationErrors [0]);
            Assert.AreEqual(ValidationError2,ex.ValidationErrors [1],ex.ValidationErrors [1]);

            //仔细检查异常消息和堆栈跟踪(由基异常独资)是preserved
            Assert.AreEqual(exceptionToString,ex.ToString(),ex.ToString());
        }

        [测试方法]
        公共无效TestDerivedSerializableExceptionWithAdditionalCustomProperty()
        {
            DerivedSerializableExceptionWithAdditionalCustomProperty EX =
                新DerivedSerializableExceptionWithAdditionalCustomProperty(信息,用户名,资源名称,validationErrors);

            //完整性检查:确保自定义属性序列化之前设置
            Assert.AreEqual(消息,ex.Message,消息);
            Assert.AreEqual(资源名称,ex.ResourceName,ex.ResourceName);
            Assert.AreEqual(2,ex.ValidationErrors.Count,ex.ValidationErrors.Count);
            Assert.AreEqual(ValidationError1,ex.ValidationErrors [0],ex.ValidationErrors [0]);
            Assert.AreEqual(ValidationError2,ex.ValidationErrors [1],ex.ValidationErrors [1]);
            Assert.AreEqual(用户名,ex.Username);

            //保存完整的ToString()值,其中包括异常消息和堆栈跟踪。
            字符串exceptionToString = ex.ToString();

            //往返异常:序列化并用的BinaryFormatter反序列化
            BinaryFormatter的BF =新的BinaryFormatter();
            使用(MemoryStream的毫秒=新的MemoryStream())
            {
                //保存对象状态
                bf.Serialize(MS,EX);

                //重新使用相同的流为反序列
                ms.Seek(0,0);

                //替换为反序列化的一个原始异常
                离=(DerivedSerializableExceptionWithAdditionalCustomProperty)bf.Deserialize(毫秒);
            }

            //确保自定义属性序列化之后pserved $ P $
            Assert.AreEqual(消息,ex.Message,消息);
            Assert.AreEqual(资源名称,ex.ResourceName,ex.ResourceName);
            Assert.AreEqual(2,ex.ValidationErrors.Count,ex.ValidationErrors.Count);
            Assert.AreEqual(ValidationError1,ex.ValidationErrors [0],ex.ValidationErrors [0]);
            Assert.AreEqual(ValidationError2,ex.ValidationErrors [1],ex.ValidationErrors [1]);
            Assert.AreEqual(用户名,ex.Username);

            //仔细检查异常消息和堆栈跟踪(由基异常独资)是preserved
            Assert.AreEqual(exceptionToString,ex.ToString(),ex.ToString());
        }
    }
}
 

More specifically, when the exception contains custom objects which may or may not themselves be serializable.

Take this example:

public class MyException : Exception
{
    private readonly string resourceName;
    private readonly IList<string> validationErrors;

    public MyException(string resourceName, IList<string> validationErrors)
    {
        this.resourceName = resourceName;
        this.validationErrors = validationErrors;
    }

    public string ResourceName
    {
        get { return this.resourceName; }
    }

    public IList<string> ValidationErrors
    {
        get { return this.validationErrors; }
    }
}

If this Exception is serialized and de-serialized, the two custom properties (ResourceName and ValidationErrors) will not be preserved. The properties will return null.

Is there a common code pattern for implementing serialization for custom exception?

解决方案

Base implementation, without custom properties

SerializableExceptionWithoutCustomProperties.cs:

namespace SerializableExceptions
{
    using System;
    using System.Runtime.Serialization;

    [Serializable]
    // Important: This attribute is NOT inherited from Exception, and MUST be specified 
    // otherwise serialization will fail with a SerializationException stating that
    // "Type X in Assembly Y is not marked as serializable."
    public class SerializableExceptionWithoutCustomProperties : Exception
    {
        public SerializableExceptionWithoutCustomProperties()
        {
        }

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

        public SerializableExceptionWithoutCustomProperties(string message, Exception innerException) 
            : base(message, innerException)
        {
        }

        // Without this constructor, deserialization will fail
        protected SerializableExceptionWithoutCustomProperties(SerializationInfo info, StreamingContext context) 
            : base(info, context)
        {
        }
    }
}

Full implementation, with custom properties

Complete implementation of a custom serializable exception (MySerializableException), and a derived sealed exception (MyDerivedSerializableException).

The main points about this implementation are summarized here:

  1. You must decorate each derived class with the [Serializable] attribute — This attribute is not inherited from the base class, and if it is not specified, serialization will fail with a SerializationException stating that "Type X in Assembly Y is not marked as serializable."
  2. You must implement custom serialization. The [Serializable] attribute alone is not enough — Exception implements ISerializable which means your derived classes must also implement custom serialization. This involves two steps:

    1. Provide a serialization constructor. This constructor should be private if your class is sealed, otherwise it should be protected to allow access to derived classes.
    2. Override GetObjectData() and make sure you call through to base.GetObjectData(info, context) at the end, in order to let the base class save its own state.

SerializableExceptionWithCustomProperties.cs:

namespace SerializableExceptions
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Security.Permissions;

    [Serializable]
    // Important: This attribute is NOT inherited from Exception, and MUST be specified 
    // otherwise serialization will fail with a SerializationException stating that
    // "Type X in Assembly Y is not marked as serializable."
    public class SerializableExceptionWithCustomProperties : Exception
    {
        private readonly string resourceName;
        private readonly IList<string> validationErrors;

        public SerializableExceptionWithCustomProperties()
        {
        }

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

        public SerializableExceptionWithCustomProperties(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        public SerializableExceptionWithCustomProperties(string message, string resourceName, IList<string> validationErrors)
            : base(message)
        {
            this.resourceName = resourceName;
            this.validationErrors = validationErrors;
        }

        public SerializableExceptionWithCustomProperties(string message, string resourceName, IList<string> validationErrors, Exception innerException)
            : base(message, innerException)
        {
            this.resourceName = resourceName;
            this.validationErrors = validationErrors;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        // Constructor should be protected for unsealed classes, private for sealed classes.
        // (The Serializer invokes this constructor through reflection, so it can be private)
        protected SerializableExceptionWithCustomProperties(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            this.resourceName = info.GetString("ResourceName");
            this.validationErrors = (IList<string>)info.GetValue("ValidationErrors", typeof(IList<string>));
        }

        public string ResourceName
        {
            get { return this.resourceName; }
        }

        public IList<string> ValidationErrors
        {
            get { return this.validationErrors; }
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }

            info.AddValue("ResourceName", this.ResourceName);

            // Note: if "List<T>" isn't serializable you may need to work out another
            //       method of adding your list, this is just for show...
            info.AddValue("ValidationErrors", this.ValidationErrors, typeof(IList<string>));

            // MUST call through to the base class to let it save its own state
            base.GetObjectData(info, context);
        }
    }
}

DerivedSerializableExceptionWithAdditionalCustomProperties.cs:

namespace SerializableExceptions
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Security.Permissions;

    [Serializable]
    public sealed class DerivedSerializableExceptionWithAdditionalCustomProperty : SerializableExceptionWithCustomProperties
    {
        private readonly string username;

        public DerivedSerializableExceptionWithAdditionalCustomProperty()
        {
        }

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

        public DerivedSerializableExceptionWithAdditionalCustomProperty(string message, Exception innerException) 
            : base(message, innerException)
        {
        }

        public DerivedSerializableExceptionWithAdditionalCustomProperty(string message, string username, string resourceName, IList<string> validationErrors) 
            : base(message, resourceName, validationErrors)
        {
            this.username = username;
        }

        public DerivedSerializableExceptionWithAdditionalCustomProperty(string message, string username, string resourceName, IList<string> validationErrors, Exception innerException) 
            : base(message, resourceName, validationErrors, innerException)
        {
            this.username = username;
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        // Serialization constructor is private, as this class is sealed
        private DerivedSerializableExceptionWithAdditionalCustomProperty(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            this.username = info.GetString("Username");
        }

        public string Username
        {
            get { return this.username; }
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }
            info.AddValue("Username", this.username);
            base.GetObjectData(info, context);
        }
    }
}


Unit Tests

MSTest unit tests for the three exception types defined above.

UnitTests.cs:

namespace SerializableExceptions
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class UnitTests
    {
        private const string Message = "The widget has unavoidably blooped out.";
        private const string ResourceName = "Resource-A";
        private const string ValidationError1 = "You forgot to set the whizz bang flag.";
        private const string ValidationError2 = "Wally cannot operate in zero gravity.";
        private readonly List<string> validationErrors = new List<string>();
        private const string Username = "Barry";

        public UnitTests()
        {
            validationErrors.Add(ValidationError1);
            validationErrors.Add(ValidationError2);
        }

        [TestMethod]
        public void TestSerializableExceptionWithoutCustomProperties()
        {
            Exception ex =
                new SerializableExceptionWithoutCustomProperties(
                    "Message", new Exception("Inner exception."));

            // Save the full ToString() value, including the exception message and stack trace.
            string exceptionToString = ex.ToString();

            // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                // "Save" object state
                bf.Serialize(ms, ex);

                // Re-use the same stream for de-serialization
                ms.Seek(0, 0);

                // Replace the original exception with de-serialized one
                ex = (SerializableExceptionWithoutCustomProperties)bf.Deserialize(ms);
            }

            // Double-check that the exception message and stack trace (owned by the base Exception) are preserved
            Assert.AreEqual(exceptionToString, ex.ToString(), "ex.ToString()");
        }

        [TestMethod]
        public void TestSerializableExceptionWithCustomProperties()
        {
            SerializableExceptionWithCustomProperties ex = 
                new SerializableExceptionWithCustomProperties(Message, ResourceName, validationErrors);

            // Sanity check: Make sure custom properties are set before serialization
            Assert.AreEqual(Message, ex.Message, "Message");
            Assert.AreEqual(ResourceName, ex.ResourceName, "ex.ResourceName");
            Assert.AreEqual(2, ex.ValidationErrors.Count, "ex.ValidationErrors.Count");
            Assert.AreEqual(ValidationError1, ex.ValidationErrors[0], "ex.ValidationErrors[0]");
            Assert.AreEqual(ValidationError2, ex.ValidationErrors[1], "ex.ValidationErrors[1]");

            // Save the full ToString() value, including the exception message and stack trace.
            string exceptionToString = ex.ToString();

            // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                // "Save" object state
                bf.Serialize(ms, ex);

                // Re-use the same stream for de-serialization
                ms.Seek(0, 0);

                // Replace the original exception with de-serialized one
                ex = (SerializableExceptionWithCustomProperties)bf.Deserialize(ms);
            }

            // Make sure custom properties are preserved after serialization
            Assert.AreEqual(Message, ex.Message, "Message");
            Assert.AreEqual(ResourceName, ex.ResourceName, "ex.ResourceName");
            Assert.AreEqual(2, ex.ValidationErrors.Count, "ex.ValidationErrors.Count");
            Assert.AreEqual(ValidationError1, ex.ValidationErrors[0], "ex.ValidationErrors[0]");
            Assert.AreEqual(ValidationError2, ex.ValidationErrors[1], "ex.ValidationErrors[1]");

            // Double-check that the exception message and stack trace (owned by the base Exception) are preserved
            Assert.AreEqual(exceptionToString, ex.ToString(), "ex.ToString()");
        }

        [TestMethod]
        public void TestDerivedSerializableExceptionWithAdditionalCustomProperty()
        {
            DerivedSerializableExceptionWithAdditionalCustomProperty ex = 
                new DerivedSerializableExceptionWithAdditionalCustomProperty(Message, Username, ResourceName, validationErrors);

            // Sanity check: Make sure custom properties are set before serialization
            Assert.AreEqual(Message, ex.Message, "Message");
            Assert.AreEqual(ResourceName, ex.ResourceName, "ex.ResourceName");
            Assert.AreEqual(2, ex.ValidationErrors.Count, "ex.ValidationErrors.Count");
            Assert.AreEqual(ValidationError1, ex.ValidationErrors[0], "ex.ValidationErrors[0]");
            Assert.AreEqual(ValidationError2, ex.ValidationErrors[1], "ex.ValidationErrors[1]");
            Assert.AreEqual(Username, ex.Username);

            // Save the full ToString() value, including the exception message and stack trace.
            string exceptionToString = ex.ToString();

            // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                // "Save" object state
                bf.Serialize(ms, ex);

                // Re-use the same stream for de-serialization
                ms.Seek(0, 0);

                // Replace the original exception with de-serialized one
                ex = (DerivedSerializableExceptionWithAdditionalCustomProperty)bf.Deserialize(ms);
            }

            // Make sure custom properties are preserved after serialization
            Assert.AreEqual(Message, ex.Message, "Message");
            Assert.AreEqual(ResourceName, ex.ResourceName, "ex.ResourceName");
            Assert.AreEqual(2, ex.ValidationErrors.Count, "ex.ValidationErrors.Count");
            Assert.AreEqual(ValidationError1, ex.ValidationErrors[0], "ex.ValidationErrors[0]");
            Assert.AreEqual(ValidationError2, ex.ValidationErrors[1], "ex.ValidationErrors[1]");
            Assert.AreEqual(Username, ex.Username);

            // Double-check that the exception message and stack trace (owned by the base Exception) are preserved
            Assert.AreEqual(exceptionToString, ex.ToString(), "ex.ToString()");
        }
    }
}

这篇关于什么是正确的方法,使一个自定义的.NET异常序列化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆