在不同的.NET版本中,将System.Data.DataSet序列化为XML,并对SQL Server的 UniqueIdentifier进行序列化与反序列化

后端开发 2026-07-12

我们有一个遗留应用,最初是用OLEDB直接连接SQL Server数据库来开发的。

自那时起,我们引入了Azure SQL数据库(但仍有部分客户在本地部署),我们正在开发一个Web应用(但大多数客户仍在使用遗留应用),并且我们也在处理一些希望使用Azure SQL数据库但拒绝为OLEDB打开端口以连接它们的客户。因此,我们编写了一个返回序列化为XML的数据集的REST API。(在经历了若干次转向JSON的尝试但最终都以失败告终之后。)

将DataSet序列化为XML并进行反序列化,结果非常简单,并且在包含架构信息时几乎在大多数情况下都能完美工作。直到我们遇到一个DataSet,其中包含一个定义为 UNIQUEIDENTIFIER 的列,即一个Guid。

我认为问题的根本原因是REST API是为.NET Core编写的,而遗留应用是为.NET Framework 4.8编写的。

虽然“简单”数据类型都没问题,但Guid的模式信息包含方式如下所示:

<xs:element name="TransactionId" 
            msdata:DataType="System.Guid, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" />

反序列化器不喜欢那种格式,于是我回退到使用OLEDB连接,执行相同的过程,并在.NET Framework 4.8应用程序中通过调试器手动对其进行序列化,我看到相同的信息被序列化为如下所示:

<xs:element name="TransactionId" 
            msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" />

幸运的是,我们并不使用大量非“简单”数据类型,除了 UNIQUEIDENTIFIER 类型之外,因此我本来打算让REST API按其原样工作;收到响应时再检查是否包含

msdata:DataType="System.Guid, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"

并将其替换为

msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

在我把XML反序列化回DataSet之前,这确实可以完美工作。但是……如果我们的REST API的开发者某天决定切换到.NET Core 9或其他版本呢?我强烈怀疑,至少,这将把他们的响应文本从

System.Private.CoreLib, Version=8.0.0.0

改为,比如

System.Private.CoreLib, Version=9.0.0.0

我更希望通过让REST API开发者在返回System.Guid架构信息时采用

System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

格式来避免这种情况的发生……

我的问题是:是否存在一种“被认可的”或“正确”的做法,还是说没有比让开发者在切换到不同的.NET Core版本时检查输出并自行编写string.Replace更好的办法?

解决方案

理论上你可以在反序列化端对导致问题的相关类型使用一堆程序集绑定。但最稳妥的办法可能是在序列化时就把类型中的程序集名称省略掉。

如果你能够控制序列化端,有机会修改模式编写器的行为。遗憾的是,这个功能在 WriteXml 函数上不可用,只有在 WriteXmlSchema 函数上可用,因此你需要手动组装XML。

using (var xr = XmlWriter.Create(yourStreamWriter, new XmlWriterSettings { Indent = true }))
{
    xr.WriteStartDocument();
    xr.WriteStartElement(ds.DataSetName);
    ds.WriteXmlSchema(xr,
        // if the type code is Object (so not a basic type) and it's an mscorlib type
        t => Type.GetTypeCode(t) == TypeCode.Object && t.Assembly == typeof(string).Assembly
            ? t.FullName    // name without assembly
            : null);
    foreach (DataTable table in ds.Tables)
    {
        table.WriteXml(xr);
    }
    xr.WriteEndElement();
}

dotnetfiddle

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章