Skip to content

Commit

Permalink
Add feature switch to disable DataSet XML serialization (dotnet#107713)
Browse files Browse the repository at this point in the history
Fixes dotnet#107704 by introducing a
feature switch that makes the DataSet and DataTable implementation of
IXmlSerializable throw. This prevents unactionable warnings from the
DataSet/DataTable implementation details when both DataSet and
IXmlSerializable happen to be used in an app.

The downside of this approach is that the trim warnings are replaced
by a runtime failure. The advantage is that it eliminates unactionable
warnings. It is preferable over annotating IXmlSerializable methods
because that interface is not inherently trim-incompatible (some
implementations might be compatible).
  • Loading branch information
sbomer authored Sep 13, 2024
1 parent 924fc2a commit 3e5d080
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/workflow/trimming/feature-switches.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif
| VerifyDependencyInjectionOpenGenericServiceTrimmability | Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability | When set to true, DependencyInjection will verify trimming annotations applied to open generic services are correct. |
| _AggressiveAttributeTrimming | System.AggressiveAttributeTrimming | When set to true, aggressively trims attributes to allow for the most size savings possible, even if it could result in runtime behavior changes |
| _ComObjectDescriptorSupport | System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported | When set to true, supports creating a TypeDescriptor based view of COM objects. |
| _DataSetXmlSerializationSupport | System.Data.DataSet.XmlSerializationIsSupported | When set to false, DataSet implementation of IXmlSerializable will throw instead of using trim-incompatible XML serialization. |
| _DefaultValueAttributeSupport | System.ComponentModel.DefaultValueAttribute.IsSupported | When set to true, supports creating a DefaultValueAttribute at runtime. |
| _DesignerHostSupport | System.ComponentModel.Design.IDesignerHost.IsSupported | When set to true, supports creating design components at runtime. |
| _EnableConsumingManagedCodeFromNativeHosting | System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting | Getting a managed function from native hosting is disabled when set to false and related functionality can be trimmed. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,4 +527,5 @@
<data name="DataSetLinq_CannotCompareDeletedRow" xml:space="preserve"><value>The DataRowComparer does not work with DataRows that have been deleted since it only compares current values.</value></data>
<data name="DataSetLinq_CannotLoadDeletedRow" xml:space="preserve"><value>The source contains a deleted DataRow that cannot be copied to the DataTable.</value></data>
<data name="DataSetLinq_NonNullableCast" xml:space="preserve"><value>Cannot cast DBNull. Value to type '{0}'. Please use a nullable type.</value></data>
<data name="DataSet_XmlSerializationUnsupported" xml:space="preserve"><value>DataSet implementation of IXmlSerializable is not trim compatible and has been disabled in the app configuration.</value></data>
</root>
27 changes: 21 additions & 6 deletions src/libraries/System.Data.Common/src/System/Data/DataSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public class DataSet : MarshalByValueComponent, IListSource, IXmlSerializable, I
internal bool _useDataSetSchemaOnly; // UseDataSetSchemaOnly , for YUKON
internal bool _udtIsWrapped; // if UDT is wrapped , for YUKON

[FeatureSwitchDefinition("System.Data.DataSet.XmlSerializationIsSupported")]
[FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))]
#pragma warning disable IL4000
internal static bool XmlSerializationIsSupported => AppContext.TryGetSwitch("System.Data.DataSet.XmlSerializationIsSupported", out bool isSupported) ? isSupported : true;
#pragma warning restore IL4000

/// <summary>
/// Initializes a new instance of the <see cref='System.Data.DataSet'/> class.
/// </summary>
Expand Down Expand Up @@ -3459,6 +3465,11 @@ public static XmlSchemaComplexType GetDataSetSchema(XmlSchemaSet? schemaSet)

XmlSchema? IXmlSerializable.GetSchema()
{
if (!XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

if (GetType() == typeof(DataSet))
{
return null;
Expand All @@ -3469,9 +3480,7 @@ public static XmlSchemaComplexType GetDataSetSchema(XmlSchemaSet? schemaSet)
XmlWriter writer = new XmlTextWriter(stream, null);
if (writer != null)
{
#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
WriteXmlSchema(this, writer);
#pragma warning restore IL2026
}
stream.Position = 0;
return XmlSchema.Read(new XmlTextReader(stream), null);
Expand All @@ -3485,6 +3494,11 @@ private static void WriteXmlSchema(DataSet ds, XmlWriter writer)

void IXmlSerializable.ReadXml(XmlReader reader)
{
if (!XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

bool fNormalization = true;
XmlTextReader? xmlTextReader = null;
IXmlTextParser? xmlTextParser = reader as IXmlTextParser;
Expand All @@ -3503,9 +3517,7 @@ void IXmlSerializable.ReadXml(XmlReader reader)
}
}

#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
ReadXmlSerializableInternal(reader);
#pragma warning restore IL2026

if (xmlTextParser != null)
{
Expand All @@ -3525,9 +3537,12 @@ private void ReadXmlSerializableInternal(XmlReader reader)

void IXmlSerializable.WriteXml(XmlWriter writer)
{
#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
if (!XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

WriteXmlInternal(writer);
#pragma warning restore IL2026
}

[RequiresUnreferencedCode("DataSet.WriteXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")]
Expand Down
26 changes: 19 additions & 7 deletions src/libraries/System.Data.Common/src/System/Data/DataTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6664,9 +6664,15 @@ public static XmlSchemaComplexType GetDataTableSchema(XmlSchemaSet? schemaSet)
return type;
}

#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
XmlSchema? IXmlSerializable.GetSchema() => GetXmlSchema();
#pragma warning restore IL2026
XmlSchema? IXmlSerializable.GetSchema()
{
if (!DataSet.XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

return GetXmlSchema();
}

[RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2046:UnrecognizedReflectionPattern",
Expand All @@ -6693,16 +6699,19 @@ public static XmlSchemaComplexType GetDataTableSchema(XmlSchemaSet? schemaSet)

void IXmlSerializable.ReadXml(XmlReader reader)
{
if (!DataSet.XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

IXmlTextParser? textReader = reader as IXmlTextParser;
bool fNormalization = true;
if (textReader != null)
{
fNormalization = textReader.Normalized;
textReader.Normalized = false;
}
#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
ReadXmlSerializableInternal(reader);
#pragma warning restore IL2026

if (textReader != null)
{
Expand All @@ -6718,9 +6727,12 @@ private void ReadXmlSerializableInternal(XmlReader reader)

void IXmlSerializable.WriteXml(XmlWriter writer)
{
#pragma warning disable IL2026 // suppressed in ILLink.Suppressions.LibraryBuild.xml
if (!DataSet.XmlSerializationIsSupported)
{
throw new NotSupportedException(SR.DataSet_XmlSerializationUnsupported);
}

WriteXmlInternal(writer);
#pragma warning restore IL2026
}

[RequiresUnreferencedCode("DataTable.WriteXml uses XmlSerialization underneath which is not trimming safe. Members from serialized types may be trimmed if not referenced directly.")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Data;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace DataSetXmlSerializationTrimmingTests
{
class Program
{
static int Main(string[] args)
{
IXmlSerializable xmlSerializable = new DataSet();

// Calling GetSchema should throw NotSupportedException
try
{
xmlSerializable.GetSchema();
return -1;
}
catch (NotSupportedException)
{
}

// Calling ReadXml should throw NotSupportedException
try
{
xmlSerializable.ReadXml(null);
return -2;
}
catch (NotSupportedException)
{
}

// Calling WriteXml should throw NotSupportedException
try
{
xmlSerializable.WriteXml(null);
return -3;
}
catch (NotSupportedException)
{
}

xmlSerializable = new DataTable();

// Calling GetSchema should throw NotSupportedException
try
{
xmlSerializable.GetSchema();
return -4;
}
catch (NotSupportedException)
{
}

// Calling ReadXml should throw NotSupportedException
try
{
xmlSerializable.ReadXml(null);
return -5;
}
catch (NotSupportedException)
{
}

// Calling WriteXml should throw NotSupportedException
try
{
xmlSerializable.WriteXml(null);
return -6;
}
catch (NotSupportedException)
{
}

return 100;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<ItemGroup>
<TestConsoleAppSourceFiles Include="CreateSqlXmlReader.cs" />
<TestConsoleAppSourceFiles Include="DbConnectionStringBuilder.cs" />
<TestConsoleAppSourceFiles Include="DataSetXmlSerialization.cs" >
<DisabledFeatureSwitches>System.Data.DataSet.XmlSerializationIsSupported</DisabledFeatureSwitches>
</TestConsoleAppSourceFiles>
</ItemGroup>

<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<_DefaultValueAttributeSupport Condition="'$(_DefaultValueAttributeSupport)' == ''">false</_DefaultValueAttributeSupport>
<DynamicCodeSupport Condition="'$(DynamicCodeSupport)' == ''">true</DynamicCodeSupport>
<UseSystemResourceKeys Condition="'$(UseSystemResourceKeys)' == ''">false</UseSystemResourceKeys>
<_DataSetXmlSerializationSupport Condition="'$(_DataSetXmlSerializationSupport)' == ''">false</_DataSetXmlSerializationSupport>
</PropertyGroup>


Expand Down

0 comments on commit 3e5d080

Please sign in to comment.