Стабильность сериализации.NET в разных версиях фреймворка
Проект, над которым я работаю, требует сериализации структуры данных перед завершением работы и восстанавливает ее состояние из этих сериализованных данных при повторном запуске.
В прошлом году мы создавали для.NET 1.1 и столкнулись с проблемой
- наш код работал на.NET 2.0
- клиент обновил программное обеспечение, которое каким-то образом установило 1.1 по умолчанию
- наш код работал на.NET 1.1 и не смог десериализовать его сохраненное состояние
Эта конкретная проблема была "решена" путем запрета данного обновления программного обеспечения, и теперь она не должна быть проблемой, поскольку мы нацелены на платформу.NET 2.0 (поэтому мы не можем работать на версии 1.1).
Какова вероятность того, что эта сериализация может снова несовместимо измениться между 2.0 и более новыми фреймворками? Если мы используем <supportedVersion>
чтобы исправить наш код на 2.0.50727, каковы шансы изменений между 2.0.50727.1434 и 2.0.50727.nnnn (какой-то будущий выпуск)? Структурируемые структуры данных - это массивы, карты, строки и так далее из стандартных библиотек классов.
Кроме того, гарантируется ли, что инфраструктура 2.0.50727 будет всегда установлена даже после дальнейших обновлений.NET? Указатели на документацию Microsoft приветствуются.
7 ответов
Шансы (но не нулевые!) Малы, что будут изменения между версиями фреймворка. Предполагается, что вы сможете использовать двоичную сериализацию и удаленное взаимодействие для связи между клиентом и сервером, работающим под разными версиями платформы. Несовместимость.NET 1.x и 2.0 - это ошибка, для которой доступно исправление.
Однако у двоичной сериализации есть и другие проблемы, особенно слабая поддержка управления версиями сериализуемой структуры. Из описанного вами варианта использования сериализация Xml является очевидным выбором: DataContractSerializer более гибок, чем XmlSerializer, если вы не возражаете против зависимости от.NET 3.x.
Вы не можете гарантировать, что.NET Framework 2.0 всегда будет установлен в будущих версиях Windows. Но я уверен, что Microsoft приложит все усилия, чтобы большинство приложений.NET 2.0 работали без изменений в.NET 4.x и более поздних версиях. У меня нет никаких ссылок на это: любое такое обязательство в любом случае действительно применимо только к следующей версии Windows (Windows 7).
Практическое правило, как правило, заключается в следующем: сериализация XML должна быть в состоянии пережить новые версии платформы и, следовательно, может храниться в течение длительного времени, но двоичная сериализация не может (и, следовательно, должна быть только временной).
Какой сериализатор вы используете? Во многих отношениях сериализатор, такой как XmlSerializer или DataContractSerializer, буферизует вас из многих деталей и предоставляет более простые варианты расширяемости. В какой-то момент новая CLR-версия, несомненно, будет необходима - поэтому я не думаю, что кто-либо может дать какие-либо гарантии относительно 2.0.50727; Вы должны быть в безопасности в краткосрочной перспективе, хотя. И я бы надеялся на меньшее количество переломных изменений...
[обновлено следующее примечание к другому ответу]
Если вам нужен двоичный формат по причинам пространства / производительности, тогда другой вариант - использовать другой двоичный сериализатор. Например, protobuf-net работает на всех вариантах.NET *, но двоичный формат (разработанный Google) является кросс-платформенным (Java, C++ и т. Д.), Что делает его очень переносимым, быстрым и небольшим.
* = Я не пробовал это на микро фреймворке, но поддерживаются CF, Silverlight, Mono, .NET 2.0 и т. Д.
Если проблема заключается в совместимости, то интерфейс ISerializable может стать тем лекарством, которое вы ищете. Этот интерфейс дает вам больше контроля над тем, как элементы сериализуются. Для получения дополнительной информации попробуйте эту статью на MSDN.
У меня есть две вещи, чтобы добавить к другим ответам...
Во-первых, использование пользовательского SerializationBinder поможет вам преодолеть множество трудностей при импорте устаревших сериализованных данных.
Во-вторых, я считаю обязательным писать обширные модульные тесты для любых сохраняемых данных. Я всегда делаю два теста, в частности:
- Проверка в оба конца - можете ли вы сериализовать и десериализовать ваши объекты и получить в точности то же самое?
- Устаревший тест импорта - убедитесь, что у вас есть версии сериализованных данных, экспортированные из каждой выпущенной версии вашего приложения. Импортируйте данные и убедитесь, что все возвращается, как и ожидалось.
Теоретически XML всегда должен быть переносимым между различными версиями .NET Framework/.NET Core/.NET. Но всегда есть исключения из правил.
Например, ЕСЛИ вы переезжаетеSystem.Data.DataTable
s в формате XML, и ЕСЛИ некоторые из этих таблиц данных содержат столбцы типа , то вас, вероятно, ждет сюрприз! Рассмотрим следующий пример кода:
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: There is an error in XML document (.., ...)
упаковкаSystem.ArgumentException: Column requires a valid DataType
когда несовместимая среда выполнения пытается его десериализовать.
Вывод .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>
Вам не нужно использовать XML для повышения гибкости и управления версиями.
Я использовал библиотеку с открытым исходным кодом Саймона Хьюитта, см. Оптимизация сериализации в.NET - часть 2 вместо сериализации.NET по умолчанию. Он предлагает некоторую автоматизацию, но, по сути, вы можете контролировать поток информации, который сериализуется и десериализуется. Для управления версиями (файл) версия может быть сначала сериализована, и во время десериализации способ интерпретации потока информации зависит от версии.
Это довольно просто сделать, хотя и несколько утомительно из-за явной сериализации / десериализации.
В качестве бонуса он в 20-40 раз быстрее и занимает меньше места для больших наборов данных (но может быть неважным в вашем случае).