Управление версиями контракта данных

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

Критические и неразрывные изменения

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

Любые изменения типа, которые не влияют на способ его передачи и получения, являются несущественными. Такие изменения не изменяют контракт данных, а только изменяют базовый тип. Например, вы можете изменить имя поля без разрывов, если затем установите свойство Name на имя более старой версии DataMemberAttribute. В следующем коде показана версия 1 контракта данных.

// Version 1
[DataContract]
public class Person
{
    [DataMember]
    private string Phone;
}
' Version 1
<DataContract()> _
Public Class Person
    <DataMember()> _
    Private Phone As String
End Class

В следующем коде показано неразрывное изменение.

// Version 2. This is a non-breaking change because the data contract
// has not changed, even though the type has.
[DataContract]
public class Person
{
    [DataMember(Name = "Phone")]
    private string Telephone;
}
' Version 2. This is a non-breaking change because the data contract 
' has not changed, even though the type has.
<DataContract()> _
Public Class Person
    <DataMember(Name:="Phone")> _
    Private Telephone As String
End Class

Некоторые изменения действительно модифицируют передаваемые данные, но могут не быть критичными. Следующие изменения всегда приводят к сбоям.

  • Изменение значения Name или Namespace контракта данных.

  • Изменение порядка элементов данных с помощью Order свойства объекта DataMemberAttribute.

  • Переименование элемента данных.

  • Изменение контракта элемента данных. Например, изменение типа элемента данных из целого числа в строку или из типа с контрактом данных "Customer" на тип с контрактом данных с именем "Person".

Кроме того, возможны следующие изменения.

Добавление и удаление элементов данных

В большинстве случаев добавление или удаление элемента данных не является критическим изменением, если только вам не требуется строгая проверка схемы (чтобы новые экземпляры соответствовали старой схеме).

Если тип с дополнительным полем десериализуется в тип с отсутствующим полем, дополнительные сведения игнорируются. (Он также может храниться в целях круговой передачи; дополнительные сведения см. вForward-Compatible контрактах данных).

Если тип с отсутствующим полем десериализуется в тип с дополнительным полем, дополнительное поле остается в значении по умолчанию, обычно ноль или null. (Значение по умолчанию может быть изменено; дополнительные сведения см. в разделе Version-Tolerant обратных вызовов сериализации.)

Например, можно использовать класс CarV1 для клиента и класс CarV2 на службе или использовать класс CarV1 на службе и класс CarV2 для клиента.

// Version 1 of a data contract, on machine V1.
[DataContract(Name = "Car")]
public class CarV1
{
    [DataMember]
    private string Model;
}

// Version 2 of the same data contract, on machine V2.
[DataContract(Name = "Car")]
public class CarV2
{
    [DataMember]
    private string Model;

    [DataMember]
    private int HorsePower;
}
' Version 1 of a data contract, on machine V1.
<DataContract(Name:="Car")> _
Public Class CarV1
    <DataMember()> _
    Private Model As String
End Class

' Version 2 of the same data contract, on machine V2.
<DataContract(Name:="Car")> _
Public Class CarV2
    <DataMember()> _
    Private Model As String

    <DataMember()> _
    Private HorsePower As Integer
End Class

Конечная точка версии 2 может успешно отправлять данные в конечную точку версии 1. Сериализация версии 2 Car контракта данных дает XML-код, аналогичный следующему.

<Car>
    <Model>Porsche</Model>
    <HorsePower>300</HorsePower>
</Car>

Подсистема десериализации в версии 1 не находит соответствующий элемент данных для HorsePower поля и удаляет эти данные.

Кроме того, конечная точка версии 1 может отправлять данные в конечную точку версии 2. Сериализация версии 1 Car контракта данных дает XML-код, аналогичный следующему.

<Car>
    <Model>Porsche</Model>
</Car>

Десериализатор версии 2 не знает, что нужно задать HorsePower для поля, так как в входящих XML-файлах нет совпадающих данных. Вместо этого поле имеет значение по умолчанию 0.

Обязательные элементы данных

Элемент данных может быть помечен как обязательный, задав для свойства IsRequired значение DataMemberAttribute. Если необходимые данные отсутствуют во время десериализации, исключение создается вместо задания элемента данных значением по умолчанию.

Добавление необходимого элемента данных является критическим изменением. То есть более новый тип по-прежнему можно отправлять в конечные точки с более старым типом, но не наоборот. Удаление элемента данных, помеченного как необходимое в любой предыдущей версии, также является критическим изменением.

IsRequired Изменение значения свойства с true на false не является критическим, но изменение с false на true может быть критическим, если какие-либо предыдущие версии типа не имеют упомянутого элемента данных.

Замечание

IsRequired Хотя для свойства задано trueзначение, входящие данные могут иметь значение NULL или ноль, и тип должен быть способен обработать эту возможность. Не используйте IsRequired в качестве механизма безопасности для защиты от плохих входящих данных.

Опущенные значения по умолчанию

Установить свойство EmitDefaultValue в атрибуте DataMemberAttribute false можно (хотя и не рекомендуется), как описано в разделе «Значения членов данных по умолчанию». Если этот параметр задан false, элемент данных не будет выдан, если он имеет значение по умолчанию (обычно null или ноль). Это несовместимо с необходимыми элементами данных в разных версиях двумя способами:

  • Контракт данных с элементом данных, который требуется в одной версии, не может получать данные по умолчанию (null или ноль) из другой версии, в которой элемент данных EmitDefaultValue задан false.

  • Обязательный элемент данных с EmitDefaultValue установленным в false не может использоваться для сериализации значения по умолчанию, NULL или нулевого значения, но может принимать такое значение при десериализации. При этом возникает проблема с обратной передачей данных (данные можно считывать, но эти же данные не могут быть записаны обратно). Таким образом, если IsRequired — это true, а EmitDefaultValue — это false в одной версии, то такое же сочетание должно применяться ко всем остальным версиям, чтобы ни одна версия контракта данных не смогла создать значение, которое не приводит к возвратному преобразованию.

Соображения по схеме

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

Схема, созданная WCF для типов договоров данных, не предусматривает возможность управления версиями. То есть схема, экспортируемая из определенной версии типа, содержит только те элементы данных, которые присутствуют в этой версии. Реализация интерфейса IExtensibleDataObject не изменяет схему для типа.

Элементы данных экспортируются в схему как необязательные элементы по умолчанию. Значение атрибута minOccurs (XML ATTRIBUTE) установлено в 0. Обязательные элементы данных экспортируются при minOccurs установленном значении 1.

Многие изменения, которые считаются неразрывными, на самом деле нарушаются, если требуется строгое соблюдение схемы. В предыдущем примере экземпляр CarV1 только с элементом Model будет соответствовать схеме CarV2 (которая имеет как Model, так и Horsepower, но оба являются необязательными). Однако обратное не верно: экземпляр CarV2 не пройдет проверку на соответствие схеме CarV1.

Циклический обход также подразумевает некоторые дополнительные аспекты. Для получения дополнительной информации см. раздел "Рассмотрение схемы" в Forward-Compatible о контрактах данных.

Другие разрешенные изменения

Реализация интерфейса IExtensibleDataObject — это изменение, не нарушающее совместимость. Однако поддержка циклического обхода не существует для версий типа до версии, в которой IExtensibleDataObject была реализована. Дополнительные сведения см. в Forward-Compatible договорах о данных.

Перечисления

Добавление или удаление элемента перечисления является критическим изменением. Изменение имени элемента перечисления нарушается, если его имя контракта не сохраняется так же, как и в старой версии с помощью атрибута EnumMemberAttribute . Дополнительные сведения см. в разделе "Типы перечисления" в контрактах данных.

Коллекции

Большинство изменений коллекции являются неразрывными, так как большинство типов коллекций взаимозаменяемы друг с другом в модели контракта данных. Однако изменение неконфигурированной коллекции на настроенную или наоборот является критическим. Кроме того, изменение параметров настройки коллекции является критическим изменением; то есть изменение имени контракта данных и пространства имен, повторяющееся имя элемента, имя ключевого элемента и имя элемента значения. Дополнительные сведения о настройке коллекции см. в разделе "Типы коллекций" в контрактах данных. Естественно, изменение контракта данных содержимого коллекции (например, изменение списка целых чисел на список строк) является критическим изменением.

См. также