Поделиться через


Независимая от версий сериализация

В платформе .NET Framework версий 1.0 и 1.1 создание сериализуемых типов, которые можно повторно использовать от одной версии приложения к другой, было проблематичным. Если тип был изменен путем добавления дополнительных полей, возникала следующая проблема:

  • Приложения старых версий выдадут исключения при запросе десериализации новых версий старого типа.

  • Приложения более новых версий выдадут исключения при десериализации старых версий типа с отсутствующими данными.

Независимая от версий сериализация (VTS) является набором функций, представленных в платформе .NET Framework 2.0, которые со временем упрощают изменение сериализуемых типов. В частности, функции VTS доступны для классов, к которым применен атрибут SerializableAttribute, включая универсальные типы. VTS позволяет добавлять новые поля для таких классов, не нарушая совместимости с другими версиями типа. Пример образца работающего приложения см. в разделе Образец технологии сериализации, независимой от версии.

Функции VTS доступны при использовании BinaryFormatter. Кроме того, при использовании SoapFormatter доступны все функции, за исключением допустимости лишних данных. Дополнительные сведения об использовании этих классов для сериализации см. в разделе Двоичная сериализация.

Список функций

Набор функций включает в себя следующие:

  • Допустимость лишних или непредвиденных данных. Это позволяет новым версиям типа отправлять данные в старые версии.

  • Допустимость отсутствующих необязательных данных. Это позволяет старым версиям отправлять данные в новые версии.

  • Обратные вызовы сериализации. Это обеспечивает интеллектуальную настройку значения по умолчанию в случае отсутствия данных.

Кроме того, существует функция объявления при добавлении нового необязательного поля. Это свойство VersionAdded атрибута OptionalFieldAttribute.

Подробное описание этих функций см. ниже.

Допустимость лишних или непредвиденных данных

В прошлом при десериализации наличие каких-либо лишних или непредвиденных данных приводило к выводу исключения. С использованием VTS в такой же ситуации какие-либо лишние или непредвиденные данные игнорируются, и исключения не выводятся. Благодаря этому приложения, использующие более новые версии типа (т.е. версию с большим числом полей), могут отправлять информацию в приложения, ожидающие старые версии этого же типа.

В следующем примере дополнительные данные в CountryField версии 2.0 класса Address игнорируются, когда более старое приложение десериализует новую версию.

// Version 1 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
}
// Version 2.0 of the Address class.
[Serializable]
public class Address
{
    public string Street;
    public string City;
    // The older application ignores this data.
    public string CountryField;
}
' Version 1 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
End Class

' Version 2.0 of the Address class.
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    ' The older application ignores this data.
    Public CountryField As String
End Class

Допустимость отсутствующих данных

Поля можно отметить как необязательные, применив к ним атрибут OptionalFieldAttribute. При отсутствии во время десериализации необязательных данных модуль сериализации игнорирует такое отсутствие и не выводит исключение. Поэтому приложения, ожидающие старые версии типа, могут отправлять данные в приложения, ожидающие новые версии этого же типа.

В следующем примере показана версия 2.0 класса Address с полем CountryField, отмеченным как необязательное. Если приложение более старой версии отправляет версию 1 в новое приложение, ожидающее версии 2.0, отсутствие данных игнорируется.

[Serializable]
public class Address
{
    public string Street;
    public string City;

    [OptionalField]
    public string CountryField;
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String

    <OptionalField> _
    Public CountryField As String
End Class

Обратные вызовы сериализации

Обратные вызовы сериализации являются механизмом, который предоставляет обработчики для процесса сериализации и десериализации в четырех точках.

Атрибут Когда вызывается связанный метод Типичные случаи использования

OnDeserializingAttribute

Перед десериализацией.*

Инициализация значений по умолчанию для необязательных полей.

OnDeserializedAttribute

После десериализации.

Исправление значений необязательных полей на основании содержимого других полей.

OnSerializingAttribute

Перед сериализацией.

Подготовка к сериализации. Например, создание структур необязательных данных.

OnSerializedAttribute

После сериализации.

Регистрация событий сериализации в журнале.

* Этот обратный вызов осуществляется перед конструктором десериализации, если такой имеется.

Использование обратных вызовов

Чтобы использовать обратные вызовы, примените соответствующий атрибут к методу, который принимает параметр StreamingContext. Для каждого из этих атрибутов можно отметить только один метод для класса. Например:

[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
    CountryField = "Japan";
}
<OnDeserializing>
Private Sub SetCountryRegionDefault(StreamingContext sc)
    CountryField = "Japan";
End Sub

Эти методы предназначены для управления версиями. Во время десериализации возможна неправильная инициализация необязательного поля, если данные для него отсутствуют. Это можно исправить, создав метод, который назначает правильное значение, а затем применяет к методу атрибут OnDeserializingAttribute или OnDeserializedAttribute.

В следующем примере показан метод в контексте типа. Если приложение более ранней версии отправляет экземпляр класса Address в приложение более поздней версии, данные поля CountryField будут отсутствовать. Но после десериализации полю будет присвоено значение по умолчанию «Japan».

[Serializable]
public class Address
{
    public string Street;
    public string City;
    [OptionalField]
    public string CountryField;

    [OnDeserializing]
    private void SetCountryRegionDefault (StreamingContext sc)
    {
        CountryField = "Japan";
    }
}
<Serializable> _
Public Class Address
    Public Street As String
    Public City As String
    <OptionalField> _
    Public CountryField As String

    <OnDeserializing> _
    Private Sub SetCountryRegionDefault(StreamingContext sc)
        CountryField = "Japan";
    End Sub
End Class

Свойство VersionAdded

OptionalFieldAttribute присвоено свойство VersionAdded. В платформе .NET Framework версии 2.0 это свойство не используется. Однако важно правильно задать это свойство, чтобы обеспечить совместимость типа с будущими модулями сериализации.

Свойство содержит сведения о том, какую версию типа добавило указанное поле. Увеличение должно осуществляться точно по одному (начиная с 2) при каждом изменении типа, см. следующий пример:

// Version 1.0
[Serializable]
public class Person
{
    public string FullName;
}

// Version 2.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded = 2)]
    public string NickName;
    [OptionalField(VersionAdded = 2)]
    public DateTime BirthDate;
}

// Version 3.0
[Serializable]
public class Person
{
    public string FullName;

    [OptionalField(VersionAdded=2)]
    public string NickName;
    [OptionalField(VersionAdded=2)]
    public DateTime BirthDate;

    [OptionalField(VersionAdded=3)]
    public int Weight;
}
' Version 1.0
<Serializable> _
Public Class Person
    Public FullName
End Class

' Version 2.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime
End Class

' Version 3.0
<Serializable> _
Public Class Person
    Public FullName As String

    <OptionalField(VersionAdded := 2)> _
    Public NickName As String
    <OptionalField(VersionAdded := 2)> _
    Public BirthDate As DateTime

    <OptionalField(VersionAdded := 3)> _
    Public Weight As Integer
End Class

SerializationBinder

Некоторым пользователям может понадобиться возможность выбирать классы для сериализации и десериализации, поскольку на сервере и на клиенте требуются различные версии класса. Абстрактный класс SerializationBinder используется для управления фактическими типами, используемыми в ходе сериализации и десериализации. Чтобы использовать этот класс, создайте класс, производный от SerializationBinder, и переопределите методы BindToName и BindToType. см. в разделе Controlling Serialization and Deserialization with SerializationBinder.

Рекомендации

Чтобы обеспечить правильное управление версиями, соблюдайте следующие правила при изменении типа от версии к версии:

  • Никогда не удаляйте сериализованное поле.

  • Никогда не применяйте атрибут NonSerializedAttribute к полю, если атрибут не был применен к полю в предыдущей версии.

  • Никогда не изменяйте имя или тип сериализованного поля.

  • При добавлении нового сериализованного поля применяйте атрибут OptionalFieldAttribute.

  • При удалении атрибута NonSerializedAttribute из поля (которое было несериализуемым в предыдущей версии) примените атрибут OptionalFieldAttribute.

  • Для всех необязательных полей присвойте значимые значения по умолчанию с помощью обратных вызовов сериализации, если 0 или NULLприемлемы в качестве значений по умолчанию.

Чтобы гарантировать совместимость типа с будущими модулями сериализации, соблюдайте следующие правила:

  • Всегда правильно задавайте свойство VersionAdded для атрибута OptionalFieldAttribute.

  • Избегайте ветвления версий.

См. также

Справочник

SerializableAttribute
BinaryFormatter
SoapFormatter
VersionAdded
OptionalFieldAttribute
OnDeserializingAttribute
OnDeserializedAttribute
OnDeserializingAttribute
OnSerializedAttribute
StreamingContext
NonSerializedAttribute

Другие ресурсы

Двоичная сериализация