I haven't dug into this one yet, but here is a small repro of an exception that comes from deserializing DbUpdateExceptions:
var formatter = new BinaryFormatter();
Exception ex;
using (var ms = new MemoryStream())
{
formatter.Serialize(ms, new DbUpdateException("foo"));
ms.Position = 0;
// Works fine
ex = (Exception)formatter.Deserialize(ms);
}
using (var ms = new MemoryStream())
{
formatter.Serialize(ms, ex);
ms.Position = 0;
// Exception happens here on 2nd deserialize
ex = (Exception)formatter.Deserialize(ms);
}
Thoughts?
Comments: This appears to be a problem with ISafeSerializationData. The issue repros with the exception defined in the example code for this interface found on MSDN here: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isafeserializationdata.aspx Full repro with the exception from the documentation is below. I will file a bug on ISafeSerializationData. ``` using System; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; using System.IO; namespace SerializationTest { [Serializable] public class NewException : Exception { // Because we don't want the exception state to be serialized normally, // we take care of that in the constructor. [NonSerialized] private NewExceptionState m_state = new NewExceptionState(); public NewException(string stringData, int intData) { // Instance data is stored directly in the exception state object. m_state.StringData = stringData; m_state.IntData = intData; // In response to SerializeObjectState, we need to provide // any state to serialize with the exception. In this // case, since our state is already stored in an // ISafeSerializationData implementation, we can // just provide that. SerializeObjectState += delegate(object exception, SafeSerializationEventArgs eventArgs) { eventArgs.AddSerializedState(m_state); }; // An alternate implementation would be to store the state // as local member variables, and in response to this // method create a new instance of an ISafeSerializationData // object and populate it with the local state here before // passing it through to AddSerializedState. } // There is no need to supply a deserialization constructor // (with SerializationInfo and StreamingContext parameters), // and no need to supply a GetObjectData implementation. // Data access is through the state object (m_State). public string StringData { get { return m_state.StringData; } } public int IntData { get { return m_state.IntData; } } // Implement the ISafeSerializationData interface // to contain custom exception data in a partially trusted // assembly. Use this interface to replace the // Exception.GetObjectData method, // which is now marked with the SecurityCriticalAttribute. [Serializable] private struct NewExceptionState : ISafeSerializationData { private string m_stringData; private int m_intData; public string StringData { get { return m_stringData; } set { m_stringData = value; } } public int IntData { get { return m_intData; } set { m_intData = value; } } // This method is called when deserialization of the // exception is complete. void ISafeSerializationData.CompleteDeserialization (object obj) { // Since the exception simply contains an instance of // the exception state object, we can repopulate it // here by just setting its instance field to be equal // to this deserialized state instance. NewException exception = obj as NewException; exception.m_state = this; } } } internal class Program { private static void Main(string[] args) { var formatter = new BinaryFormatter(); var ex = new NewException("foo", 7); using (var ms = new MemoryStream()) { formatter.Serialize(ms, ex); ms.Position = 0; // Works fine ex = (NewException)formatter.Deserialize(ms); } Console.WriteLine(ex.StringData); using (var ms = new MemoryStream()) { formatter.Serialize(ms, ex); ms.Position = 0; // Exception happens here on 2nd deserialize ex = (NewException)formatter.Deserialize(ms); } Console.WriteLine(ex.StringData); } } } ```
var formatter = new BinaryFormatter();
Exception ex;
using (var ms = new MemoryStream())
{
formatter.Serialize(ms, new DbUpdateException("foo"));
ms.Position = 0;
// Works fine
ex = (Exception)formatter.Deserialize(ms);
}
using (var ms = new MemoryStream())
{
formatter.Serialize(ms, ex);
ms.Position = 0;
// Exception happens here on 2nd deserialize
ex = (Exception)formatter.Deserialize(ms);
}
Thoughts?
Comments: This appears to be a problem with ISafeSerializationData. The issue repros with the exception defined in the example code for this interface found on MSDN here: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isafeserializationdata.aspx Full repro with the exception from the documentation is below. I will file a bug on ISafeSerializationData. ``` using System; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; using System.IO; namespace SerializationTest { [Serializable] public class NewException : Exception { // Because we don't want the exception state to be serialized normally, // we take care of that in the constructor. [NonSerialized] private NewExceptionState m_state = new NewExceptionState(); public NewException(string stringData, int intData) { // Instance data is stored directly in the exception state object. m_state.StringData = stringData; m_state.IntData = intData; // In response to SerializeObjectState, we need to provide // any state to serialize with the exception. In this // case, since our state is already stored in an // ISafeSerializationData implementation, we can // just provide that. SerializeObjectState += delegate(object exception, SafeSerializationEventArgs eventArgs) { eventArgs.AddSerializedState(m_state); }; // An alternate implementation would be to store the state // as local member variables, and in response to this // method create a new instance of an ISafeSerializationData // object and populate it with the local state here before // passing it through to AddSerializedState. } // There is no need to supply a deserialization constructor // (with SerializationInfo and StreamingContext parameters), // and no need to supply a GetObjectData implementation. // Data access is through the state object (m_State). public string StringData { get { return m_state.StringData; } } public int IntData { get { return m_state.IntData; } } // Implement the ISafeSerializationData interface // to contain custom exception data in a partially trusted // assembly. Use this interface to replace the // Exception.GetObjectData method, // which is now marked with the SecurityCriticalAttribute. [Serializable] private struct NewExceptionState : ISafeSerializationData { private string m_stringData; private int m_intData; public string StringData { get { return m_stringData; } set { m_stringData = value; } } public int IntData { get { return m_intData; } set { m_intData = value; } } // This method is called when deserialization of the // exception is complete. void ISafeSerializationData.CompleteDeserialization (object obj) { // Since the exception simply contains an instance of // the exception state object, we can repopulate it // here by just setting its instance field to be equal // to this deserialized state instance. NewException exception = obj as NewException; exception.m_state = this; } } } internal class Program { private static void Main(string[] args) { var formatter = new BinaryFormatter(); var ex = new NewException("foo", 7); using (var ms = new MemoryStream()) { formatter.Serialize(ms, ex); ms.Position = 0; // Works fine ex = (NewException)formatter.Deserialize(ms); } Console.WriteLine(ex.StringData); using (var ms = new MemoryStream()) { formatter.Serialize(ms, ex); ms.Position = 0; // Exception happens here on 2nd deserialize ex = (NewException)formatter.Deserialize(ms); } Console.WriteLine(ex.StringData); } } } ```