Description
In .NET >= 8, When a DataContractSerializer is used to serialize a nullable custom value type (Nullable<T>) and the corresponding type T has not previously been seen by a DataContractSerializer, an exception is thrown:
System.Runtime.Serialization.SerializationException: An internal error has occurred. DataContract cache overflow.
at System.Runtime.Serialization.DataContracts.DataContract.DataContractCriticalHelper.GetIdForInitialization(ClassDataContract classContract)
at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.CreateObject(ClassDataContract classContract)
at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.GenerateClassReader(ClassDataContract classContract)
at System.Runtime.Serialization.DataContracts.ClassDataContract.get_XmlFormatReaderDelegate()
at System.Runtime.Serialization.DataContracts.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
This appears to be a bug introduce in the following commit:
adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378
Specifically, the commit changed line 378 of DataContract.cs from:
if (ContractMatches(classContract, s_dataContractCache[i]))
to
if (ContractMatches(classContract, s_dataContractCache.GetItem(id)))
Note the change from i to id as the argument to GetItem(), which appears to be inadvertent.
Reproduction Steps
The following code works as expected (no exception thrown) when targetting .NET 6, but fails with an exception in .NET 8/9/10:
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
namespace DataContractSerializerTest {
internal class Program {
[DataContract]
private struct TestStruct1 {
}
static void Main(string[] args) {
// if this line is uncommented , the code compiles and runs without any exceptions.
// If it is commented out, an exception is thrown.
//SerializeAndDeserialize<TestStruct1>(default(TestStruct1));
SerializeAndDeserialize<TestStruct1?>(default(TestStruct1));
}
private static T? SerializeAndDeserialize<T>(T? obj) {
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
StringBuilder stringBuilder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(stringBuilder)) {
serializer.WriteObject(writer, obj);
}
using (XmlReader reader = XmlReader.Create(new StringReader(stringBuilder.ToString()))) {
return (T?)serializer.ReadObject(reader);
}
}
}
}
Expected behavior
Nullable<T> where T is a value type marked with [DataContract] can be serialized successfully as it was in .NET < 8.
Actual behavior
An exception is thrown:
System.Runtime.Serialization.SerializationException: An internal error has occurred. DataContract cache overflow.
at System.Runtime.Serialization.DataContracts.DataContract.DataContractCriticalHelper.GetIdForInitialization(ClassDataContract classContract)
at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.CreateObject(ClassDataContract classContract)
at System.Runtime.Serialization.XmlFormatReaderGenerator.CriticalHelper.GenerateClassReader(ClassDataContract classContract)
at System.Runtime.Serialization.DataContracts.ClassDataContract.get_XmlFormatReaderDelegate()
at System.Runtime.Serialization.DataContracts.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract)
at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
Regression?
Yes, appears to work correctly in .NET <8, and appears to have been introduced in the commit adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378
Known Workarounds
Use a DataContractSerializer to serialize the non-nullable struct T before using it to serialize Nullable<T>.
Configuration
.NET 10, Windows 11, x86.
Other information
No response
Description
In .NET >= 8, When a
DataContractSerializeris used to serialize a nullable custom value type (Nullable<T>) and the corresponding typeThas not previously been seen by aDataContractSerializer, an exception is thrown:This appears to be a bug introduce in the following commit:
adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378
Specifically, the commit changed line 378 of
DataContract.csfrom:if (ContractMatches(classContract, s_dataContractCache[i]))to
if (ContractMatches(classContract, s_dataContractCache.GetItem(id)))Note the change from
itoidas the argument toGetItem(), which appears to be inadvertent.Reproduction Steps
The following code works as expected (no exception thrown) when targetting .NET 6, but fails with an exception in .NET 8/9/10:
Expected behavior
Nullable<T>whereTis a value type marked with[DataContract]can be serialized successfully as it was in .NET < 8.Actual behavior
An exception is thrown:
Regression?
Yes, appears to work correctly in .NET <8, and appears to have been introduced in the commit adaebca#diff-343a32c8e8d13ca319b989de56100d9b829145aa2f7be2908f814e78cbe12b6eL378
Known Workarounds
Use a
DataContractSerializerto serialize the non-nullable structTbefore using it to serializeNullable<T>.Configuration
.NET 10, Windows 11, x86.
Other information
No response