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

По мере развития приложений может возникнуть необходимость в изменении контрактов данных, используемых службами.As applications evolve, you may also have to change the data contracts the services use. В данном разделе описано, как создавать версии контрактов данных.This topic explains how to version data contracts. В этом разделе описываются механизмы создания версий контрактов данных.This topic describes the data contract versioning mechanisms. Полный обзор и руководство по управлению версиями, см. в разделе рекомендации: Управление версиями контракта данных.For a complete overview and prescriptive versioning guidance, see Best Practices: Data Contract Versioning.

Сравнение критических и некритических измененийBreaking vs. Nonbreaking Changes

Изменения в контрактах данных могут быть критическими и некритическими.Changes to a data contract can be breaking or nonbreaking. Когда изменение контракта данных не является критическим, приложение, использующее старую версию контракта, может взаимодействовать с приложением, использующим новую версию, и приложение, использующее новую версию контракта, может взаимодействовать с приложением, использующим старую версию.When a data contract is changed in a nonbreaking way, an application using the older version of the contract can communicate with an application using the newer version, and an application using the newer version of the contract can communicate with an application using the older version. Напротив, критическое изменение исключает взаимодействие в одном или обоих направлениях.On the other hand, a breaking change prevents communication in one or both directions.

Любые изменения типа, не влияющие на способ его передачи и приема, являются некритическими.Any changes to a type that do not affect how it is transmitted and received are nonbreaking. Такие изменения не изменяют контракт данных, они изменяют только базовый тип.Such changes do not change the data contract, only the underlying type. Например, изменение имени поля будет некритическим, если затем задать для свойства Name атрибута DataMemberAttribute имя из старой версии.For example, you can change the name of a field in a nonbreaking way if you then set the Name property of the DataMemberAttribute to the older version name. В следующем коде показана версия 1 контракта данных.The following code shows version 1 of a data contract.

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

В следующем коде показано некритическое изменение.The following code shows a nonbreaking change.

// 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 

Некоторые изменения влияют на передаваемые данные, но могут быть как критическими, так и некритическими.Some changes do modify the transmitted data, but may or may not be breaking. Указанные ниже изменения всегда критические.The following changes are always breaking:

  • Изменение значения Name или Namespace контракта данных.Changing the Name or Namespace value of a data contract.

  • Изменение порядка членов данных с помощью свойства Order атрибута DataMemberAttribute.Changing the order of data members by using the Order property of the DataMemberAttribute.

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

  • Изменение контракта данных члена данных.Changing the data contract of a data member. Например, изменение типа члена данных с целого числа на строку или с типа, имеющего контракт данных с именем "Заказчик" на тип, имеющий контракт данных с именем "Личность".For example, changing the type of data member from an integer to a string, or from a type with a data contract named "Customer" to a type with a data contract named "Person".

Возможны также указанные ниже изменения.The following changes are also possible.

Добавление и удаление членов данныхAdding and Removing Data Members

В большинстве случаев добавление или удаление члена данных не является критическим изменением, если только не требуется строгая достоверность схемы (новые экземпляры проверяются по старой схеме).In most cases, adding or removing a data member is not a breaking change, unless you require strict schema validity (new instances validating against the old schema).

Когда тип с дополнительным полем десериализуется в тип с отсутствующим полем, дополнительная информация игнорируется.When a type with an extra field is deserialized into a type with a missing field, the extra information is ignored. (Также может сохраняться для целей кругового обращения; Дополнительные сведения, см. в разделе контракты данных обладает прямой совместимостью).(It may also be stored for round-tripping purposes; for more information, see Forward-Compatible Data Contracts).

Когда тип с отсутствующим полем десериализуется в тип с дополнительным полем, для дополнительного поля сохраняется значение по умолчанию, обычно ноль или null.When a type with a missing field is deserialized into a type with an extra field, the extra field is left at its default value, usually zero or null. (Значение по умолчанию может быть изменено; Дополнительные сведения см. в разделе независимые от версий обратные вызовы сериализации.)(The default value may be changed; for more information, see Version-Tolerant Serialization Callbacks.)

Например, можно использовать класс CarV1 в клиенте и класс CarV2 в службе или можно использовать класс CarV1 в службе и класс CarV2 в клиенте.For example, you can use the CarV1 class on a client and the CarV2 class on a service, or you can use the CarV1 class on a service and the CarV2 class on a client.

// 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.The version 2 endpoint can successfully send data to the version 1 endpoint. В результате сериализации версии 2 контракта данных Car получается код XML, аналогичный приведенному ниже.Serializing version 2 of the Car data contract yields XML similar to the following.

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

Механизм десериализации в V1 не находит соответствующего члена данных для поля HorsePower и отбрасывает эти данные.The deserialization engine on V1 does not find a matching data member for the HorsePower field, and discards that data.

Конечная точка версии 1 также может передать данные конечной точке версии 2.Also, the version 1 endpoint can send data to the version 2 endpoint. В результате сериализации версии 1 контракта данных Car получается код XML, аналогичный приведенному ниже.Serializing version 1 of the Car data contract yields XML similar to the following.

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

Десериализатор версии 2 не знает, какое значение следует присвоить полю HorsePower, поскольку во входящем коде XML нет соответствующих данных.The version 2 deserializer does not know what to set the HorsePower field to, because there is no matching data in the incoming XML. В результате этому полю присваивается значение по умолчанию "0".Instead, the field is set to the default value of 0.

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

Член данных можно пометить как обязательный, задав для свойства IsRequired атрибута DataMemberAttribute значение true.A data member may be marked as being required by setting the IsRequired property of the DataMemberAttribute to true. В случае отсутствия во время десериализации обязательных данных вместо присваивания этому члену данных значения по умолчанию генерируется исключение.If required data is missing while deserializing, an exception is thrown instead of setting the data member to its default value.

Добавление обязательного члена данных - это критическое изменение.Adding a required data member is a breaking change. То есть, новый тип можно передавать на конечные точки старого типа, но не наоборот.That is, the newer type can still be sent to endpoints with the older type, but not the other way around. Удаление члена данных, помеченного в старой версии как обязательный, также является критическим изменением.Removing a data member that was marked as required in any prior version is also a breaking change.

Изменение значения свойства IsRequired с true на false не является критическим изменением, а изменение со значения false на значение true может быть критическим, если в какой-либо предыдущей версии этого типа соответствующий член данных отсутствует.Changing the IsRequired property value from true to false is not breaking, but changing it from false to true may be breaking if any prior versions of the type do not have the data member in question.

Примечание

Даже если для свойства IsRequired задано значение true, входящие данные могут иметь значение "null" или ноль, и тип должен быть готов к обработке такой ситуации.Although the IsRequired property is set to true, the incoming data may be null or zero, and a type must be prepared to handle this possibility. Не используйте значение IsRequired в качестве механизма защиты от неправильных входных данных.Do not use IsRequired as a security mechanism to protect against bad incoming data.

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

Можно (хотя и не рекомендуется) задать EmitDefaultValue свойство атрибута DataMemberAttribute значение false, как описано в разделе значения по умолчанию членов данных.It is possible (although not recommended) to set the EmitDefaultValue property on the DataMemberAttribute attribute to false, as described in Data Member Default Values. Если задано значение false, член данных не передается, если для него установлено значение по умолчанию (обычно "null" или ноль).If this setting is false, the data member will not be emitted if it is set to its default value (usually null or zero). Это не совместимо с обязательными членами данных в различных версиях по двум причинам.This is not compatible with required data members in different versions in two ways:

  • Контракт данных, содержащий член данных, обязательный в одной версии, не может принимать данные по умолчанию ("null" или ноль) из другой версии, в которой для свойства EmitDefaultValue этого члена данных задано значение false.A data contract with a data member that is required in one version cannot receive default (null or zero) data from a different version in which the data member has EmitDefaultValue set to false.

  • Обязательный член данных, для свойства EmitDefaultValue которого задано значение false, не может использоваться для сериализации его значения по умолчанию ("null" или ноль), но может получить такое значение при десериализации.A required data member that has EmitDefaultValue set to false cannot be used to serialize its default (null or zero) value, but can receive such a value on deserialization. Это создает проблему при круговой передаче (данные можно считать, но эти же данные невозможно потом записать).This creates a round-tripping problem (data can be read in but the same data cannot then be written out). Поэтому если в одной версии для свойства IsRequired задано значение true и для свойства EmitDefaultValue задано значение false, это же сочетание должно применяться во всех остальных версиях, чтобы ни одна из версий контракта данных не могла создать значение, которое не сможет пройти круговую передачу.Therefore, if IsRequired is true and EmitDefaultValue is false in one version, the same combination should apply to all other versions such that no version of the data contract would be able to produce a value that does not result in a round trip.

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

Описание схемы, создаваемой для типов контрактов данных, см. в разделе Data Contract Schema Reference.For an explanation of what schema is produced for data contract types, see Data Contract Schema Reference.

Схема WCF создает типы контрактов данных делает не поддерживает управление версиями.The schema WCF produces for data contract types makes no provisions for versioning. Это означает, что схема, экспортированная из определенной версии типа, содержит только те члены данных, которые присутствуют в этой версии.That is, the schema exported from a certain version of a type contains only those data members present in that version. Реализация интерфейса IExtensibleDataObject не изменяет схему для типа.Implementing the IExtensibleDataObject interface does not change the schema for a type.

По умолчанию члены данных экспортируются в схему как необязательные элементы.Data members are exported to the schema as optional elements by default. То есть значение minOccurs (XML-атрибут) равно 0.That is, the minOccurs (XML attribute) value is set to 0. Необходимые члены данных экспортируются, если параметр minOccurs имеет значение 1.Required data members are exported with minOccurs set to 1.

Многие изменения, считающиеся некритическими, на самом деле являются критическими, если требуется строгое соответствие схеме.Many of the changes considered to be nonbreaking are actually breaking if strict adherence to the schema is required. В предыдущем примере экземпляр CarV1, содержащий только элемент Model, пройдет проверку по схеме CarV2 (содержащую элементы Model и Horsepower, оба необязательные).In the preceding example, a CarV1 instance with just the Model element would validate against the CarV2 schema (which has both Model and Horsepower, but both are optional). Однако обратное неверно: экземпляр CarV2 не пройдет проверку по схеме CarV1.However, the reverse is not true: a CarV2 instance would fail validation against the CarV1 schema.

Следует учитывать возможность круговой передачи.Round-tripping also entails some additional considerations. Дополнительные сведения см. в разделе «Замечания по схемам» в контракты данных обладает прямой совместимостью.For more information, see the "Schema Considerations" section in Forward-Compatible Data Contracts.

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

Реализация интерфейса IExtensibleDataObject является некритическим изменением.Implementing the IExtensibleDataObject interface is a nonbreaking change. Однако для версий типа, предшествующих версии, в которой был реализован интерфейс IExtensibleDataObject, поддержка кругового пути отсутствует.However, round-tripping support does not exist for versions of the type prior to the version in which IExtensibleDataObject was implemented. Дополнительные сведения о создании контрактов данных, обладающих прямой совместимостью, см. в разделе Контракты данных, совместимые с любыми будущими изменениями.For more information, see Forward-Compatible Data Contracts.

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

Добавление или удаление члена перечисления - это критическое изменение.Adding or removing an enumeration member is a breaking change. Изменение имени члена перечисления также является критическим изменением, за исключением случая, когда с помощью атрибута EnumMemberAttribute имя контракта оставлено таким же, как и в старой версии.Changing the name of an enumeration member is breaking, unless its contract name is kept the same as in the old version by using the EnumMemberAttribute attribute. Дополнительные сведения см. в разделе типы перечислений в контрактах данных.For more information, see Enumeration Types in Data Contracts.

КоллекцииCollections

Большинство изменений коллекций являются некритическими, так как в модели контракта данных большинство типов коллекции взаимозаменяемы.Most collection changes are nonbreaking because most collection types are interchangeable with each other in the data contract model. Однако преобразование стандартной коллекции в настраиваемую или наоборот является критическим изменением.However, making a noncustomized collection customized or vice versa is a breaking change. Итак, изменение параметров настройки коллекции является весьма важным. Сюда относится изменение ее имени контракта данных и пространства имен, имени повторяющегося элемента, имени ключевого элемента и имени элемента значения.Also, changing the collection's customization settings is a breaking change; that is, changing its data contract name and namespace, repeating element name, key element name, and value element name. Дополнительные сведения о настройке коллекции см. в разделе типы коллекций в контрактах данных.For more information about collection customization, see Collection Types in Data Contracts.
Конечно, изменение контракта данных содержимого коллекции (например, переход со списка целых чисел на список строк) является важным изменением.Naturally, changing the data contract of contents of a collection (for example, changing from a list of integers to a list of strings) is a breaking change.

См. такжеSee also