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

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

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

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

Любые изменения типа, не влияющие на способ его передачи и приема, являются некритическими. Такие изменения не изменяют контракт данных, они изменяют только базовый тип. Например, изменение имени поля будет некритическим, если затем задать для свойства 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.

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

  • Изменение контракта данных члена данных. Например, изменение типа члена данных с целого числа на строку или с типа, имеющего контракт данных с именем "Заказчик" на тип, имеющий контракт данных с именем "Личность".

Возможны также указанные ниже изменения.

Добавление и удаление членов данных

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

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

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

Например, можно использовать класс 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>  

Механизм десериализации в V1 не находит соответствующего члена данных для поля HorsePower и отбрасывает эти данные.

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

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

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

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

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

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

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

Примечание

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

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

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

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

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

Замечания по схемам

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

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

По умолчанию члены данных экспортируются в схему как необязательные элементы. То есть значение minOccurs (XML-атрибут) равно 0. Необходимые члены данных экспортируются, если параметр minOccurs имеет значение 1.

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

Следует учитывать возможность круговой передачи. Дополнительные сведения см. в разделе "рекомендации по схеме" статьи контракты данных с прямыми совместимостью.

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

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

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

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

Коллекции

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

См. также