.NET 序列化在不同框架版本之间的稳定性

发布于 2024-07-07 11:07:49 字数 598 浏览 7 评论 0原文

我正在开发的一个项目需要在关闭之前序列化数据结构,并在再次启动时从该序列化数据恢复其状态。

去年,我们正在为 .NET 1.1 进行构建,并遇到了一个棘手的问题,

  • 我们的代码在 .NET 2.0 上运行,
  • 客户使用某些软件进行升级,该软件以某种方式将 1.1 设置为默认值,
  • 我们的代码在 .NET 1.1 上运行,并且无法反序列化其存储状态

这个特定问题已通过禁止特定软件升级而“解决”,并且现在不应该成为问题,因为我们的目标是 .NET 2.0 框架(因此我们不可能在 1.1 上运行)。

这种序列化在 2.0 和更新的框架之间再次发生不兼容变化的可能性有多大? 如果我们使用 将代码修复为 2.0.50727,那么 2.0.50727.1434 和 2.0.50727.nnnn(未来的某个版本)之间发生更改的可能性有多大? 被序列化的数据结构是来自标准类库的数组、映射、字符串等。

此外,即使在进一步升级 .NET 后,是否也能保证始终安装 2.0.50727 框架? 欢迎指向 Microsoft 文档。

A project I'm working on requires serializing a data structure before shutting down and restores its state from this serialized data when it start up again.

Last year, we were building for .NET 1.1, and ran into a tricky issue where

  • our code ran on .NET 2.0
  • a customer upgraded with some software that somehow set 1.1 as default
  • our code ran on .NET 1.1 and was unable to deserialize its stored state

This particular issue was "resolved" by barring that particular software upgrade, and shouldn't be a problem now that we're targeting the .NET 2.0 framework (so we can't possibly run on 1.1).

What is the chance that this serialization could again change incompatibly between, 2.0 and newer frameworks? If we use <supportedVersion> to fix our code to 2.0.50727, what are the chances of changes between 2.0.50727.1434 and 2.0.50727.nnnn (some future release)? The data structures being serialized are arrays, maps, strings, et cetera from the standard class libraries.

Additionally, is it guaranteed that a 2.0.50727 framework will be always installed even after further .NET upgrades? Pointers to Microsoft documentation welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

安静被遗忘 2024-07-14 11:07:49

框架版本之间发生变化的可能性很低(但不是零!)。 目的是您应该能够使用二进制序列化和远程处理在运行不同框架版本的客户端和服务器之间进行通信。 .NET 1.x 和 2.0 之间的不兼容性是一个错误 有可用的补丁。

然而,二进制序列化还有其他问题,特别是对正在序列化的结构的版本控制的支持很差。 从您描述的用例来看,Xml 序列化是显而易见的选择:如果您不介意对 .NET 3.x 的依赖,DataContractSerializer 比 XmlSerializer 更灵活。

您无法保证 .NET Framework 2.0 将始终安装在未来版本的 Windows 上。 但我确信 Microsoft 将努力确保大多数 .NET 2.0 应用程序在 .NET 4.x 及更高版本上运行不变。 我对此没有任何参考:任何此类承诺在任何情况下都仅真正适用于下一版本的 Windows (Windows 7)。

The chances are low (but not zero!) that there will be changes between framework versions. The intention would be that you should be able to use binary serialization and remoting to communicate between a client and a server running different framework versions. The incompatibility between .NET 1.x and 2.0 is a bug for which a patch is available.

However binary serialization has other issues, especially poor support for versioning of the structure you're serializing. From the use case you've described, Xml serialization is the obvious choice: DataContractSerializer being more flexible than XmlSerializer if you don't mind the dependency on .NET 3.x.

You can't guarantee that the .NET framework 2.0 will always be installed on future versions of Windows. But I'm sure Microsoft will work hard to ensure that most .NET 2.0 apps will run unchanged on .NET 4.x and later versions. I don't have any references for this: any such commitment would in any case only really apply to the next version of Windows (Windows 7).

蓝眼睛不忧郁 2024-07-14 11:07:49

经验法则通常是:XML 序列化应该能够在新的框架版本中生存,因此可以长期存储,但二进制序列化不能(因此只能是暂时的)。

The rule of thumb is generally: XML serialization should be able to survive new framework versions and therefore can be stored long-term, but binary serialization cannot (and therefore should only ever be transient).

尤怨 2024-07-14 11:07:49

你使用什么序列化器? 在许多方面,像 XmlSerializer 或 DataContractSerializer 这样的序列化器可以缓冲许多细节,并提供更简单的可扩展性选项。
在某些时候,新的 CLR 版本无疑是必要的 - 所以我认为没有人可以对 2.0.50727 做出任何保证; 不过,短期内你应该是安全的。 我希望减少重大更改...

[更新了另一个回复的注释]

如果您出于空间/性能原因需要二进制格式,那么另一个选择是使用不同的二进制序列化器。 例如,protobuf-net 适用于所有 .NET 变体*,但二进制文件格式(由 Google 制定)是跨平台兼容的(Java、C++ 等)——使其非常便携、快速且小巧。

*=我还没有在微框架上尝试过,但是CF、Silverlight、Mono、.NET 2.0等都支持。

What serializer are you using? In many ways, a serializer like XmlSerializer or DataContractSerializer buffers you from many details, and provides simpler extensibility options.
At some point, a new CLR version will undoubtably be necessary - so I don't think anybody can make any guarantees about 2.0.50727; you should be safe short-term, though. And I would hope for fewer breaking changes...

[updated following note on another reply]

If you want a binary format for space/performance reasons, then another option is to use a different binary serializer. For example, protobuf-net works on all .NET variants*, but the binary format (dvised by Google) is cross-platform compatible (Java, C++, etc) - making it very portable, quick, and small.

*=I haven't tried it on micro framework, but CF, Silverlight, Mono, .NET 2.0 etc are all supported.

要走干脆点 2024-07-14 11:07:49

如果兼容性是一个问题,ISerialized界面可能是您正在寻找的解决方案。 此界面使您可以更好地控制项目的序列化方式。 有关详细信息,请尝试这篇 msdn 上的文章

If compatibility is a concern, the ISerializable interface might be the cure you're looking for. This interface gives you more control over how items are serialized. For more info try this article on msdn.

梦醒灬来后我 2024-07-14 11:07:49

我有两件事要添加到其他答案中......

首先,使用自定义 SerializationBinder 可以帮助您解决导入遗留序列化数据的许多困难。

其次,我认为必须为任何持久数据编写广泛的单元测试。 我总是特别做两个测试:

  1. 往返测试 - 你能序列化和反序列化你的对象并得到完全相同的东西吗?
  2. 旧版导入测试 - 确保您拥有从应用程序的每个发布版本导出的序列化数据版本。 导入数据并检查一切是否按预期返回。

I have two things to add to the other answers...

First, making use of a custom SerializationBinder can get you round lots of difficulties importing legacy serialized data.

Second, I consider it mandatory to write extensive unit tests for any persisted data. I always do two tests in particular:

  1. Round-trip test - can you serialize and deserialize your objects and get exactly the same thing back?
  2. Legacy import test - make sure you have versions of serialized data exported from every released version of your app. Import the data and check that everything comes back in as expected.
情绪失控 2024-07-14 11:07:49

理论上,XML 应该始终可以在不同版本的 .NET Framework/.NET Core/.NET 之间移植。 但规则总有例外。

例如,如果您以 XML 格式移动 System.Data.DataTable,并且如果其中一些数据表包含 System.Guid 类型的列,那么您'很可能会带来惊喜! 请考虑以下示例代码:

using System;
using System.Data;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

public class Program
{
    internal static string SerializeToXml<T>(T input)
    {
        var writerSettings = new XmlWriterSettings()
        {
            Encoding = new UTF8Encoding(false),
            Indent = true
        };
        using (var stream = new MemoryStream())
        using (var writer = XmlWriter.Create(stream, writerSettings))
        {
            (new XmlSerializer(typeof(T))).Serialize(writer, input);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    public static void Main()
    {
        var dt = new DataTable("Name");
        dt.Columns.Add("Example", typeof(Guid));
        Console.WriteLine(SerializeToXml(dt));
    }
}

与大多数其他系统类型不同,System.Guid 获取列定义中包含的特定于版本的 msdata:DataType 属性,并且会抛出 System.InvalidOperationException:当不兼容的运行时尝试反序列化列时,包装 System.ArgumentException:列需要有效的 DataType 的 XML 文档 (.., ...) 中存在错误。

.NET Framework 4.7.2 输出:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET Core 3.1 输出:(是的,它输出版本 4.0.0.0)

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 5 输出:

.NET 5
<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 6 输出:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 7 输出:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

In theory XML should always be portable between different versions of .NET Framework/.NET Core/.NET. But there are always exceptions to the rule.

For example IF you're moving System.Data.DataTables around in XML format, and IF some of those data tables contain columns of type System.Guid, then you're likely in for a surprise! Consider the following example code:

using System;
using System.Data;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

public class Program
{
    internal static string SerializeToXml<T>(T input)
    {
        var writerSettings = new XmlWriterSettings()
        {
            Encoding = new UTF8Encoding(false),
            Indent = true
        };
        using (var stream = new MemoryStream())
        using (var writer = XmlWriter.Create(stream, writerSettings))
        {
            (new XmlSerializer(typeof(T))).Serialize(writer, input);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    public static void Main()
    {
        var dt = new DataTable("Name");
        dt.Columns.Add("Example", typeof(Guid));
        Console.WriteLine(SerializeToXml(dt));
    }
}

Unlike most other system types System.Guid gets a version-specific msdata:DataType attribute included in the column definition and that will throw a System.InvalidOperationException: There is an error in XML document (.., ...) wrapping a System.ArgumentException: Column requires a valid DataType when an incompatible runtime attempts to deserialize it.

.NET Framework 4.7.2 output:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET Core 3.1 output: (yes, it outputs version 4.0.0.0)

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 5 output:

.NET 5
<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 6 output:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>

.NET 7 output:

<?xml version="1.0" encoding="utf-8"?>
<DataTable>
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Name" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Name">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Example" msdata:DataType="System.Guid, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e" type="xs:string" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />
</DataTable>
早乙女 2024-07-14 11:07:49

您不必使用 XML 即可获得更高的灵活性和版本控制。

我使用了 Simon Hewitt 的开源库,请参阅优化.NET 中的序列化 - 第 2 部分,而不是默认的 .NET 序列化。 它提供了一些自动化功能,但本质上您可以控制序列化和反序列化的信息流。 对于版本控制,可以首先序列化(文件)版本
反序列化时间以及解释信息流的方式取决于版本。

做起来很简单,尽管由于明确的原因而有些乏味
序列化/反序列化。

作为奖励,它的速度提高了 20-40 倍,并且对于大型数据集占用的空间更少(但对于您的情况来说可能并不重要)。

You do not have to use XML in order to gain higher flexibility and versioning.

I have used Simon Hewitt's open source library, see Optimizing Serialization in .NET - part 2 instead of default .NET serialisation. It offers some automation, but essentially you can control the stream of information that is serialised and deserialised. For versioning the (file) version can be serialised first and at
deserialisation time the way the stream of information is interpreted is dependent on the version.

It is quite simple to do, although somewhat tedious due to the explicit
serialisation / deserialisation.

As a bonus it is 20-40 times faster and takes up less space for large data sets (but may be unimportant in your case).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文