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

В данном разделе приводятся рекомендации по созданию контрактов данных, которые можно легко развить со временем. Дополнительные сведения о контрактах данных см. в разделах об использовании контрактов данных.

Замечания по проверке схемы

При обсуждении управления версиями контрактов данных важно отметить, что схема контракта данных, экспортируемая Windows Communication Foundation (WCF), не поддерживает управление версиями, кроме того, что элементы помечены как необязательные по умолчанию.

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

Однако существует много сценариев, в которых не требуется строгое соответствие схеме. Многие платформы веб-служб, включая веб-службы WCF и XML, созданные с помощью ASP.NET, не выполняют проверку схемы по умолчанию и поэтому допускают дополнительные элементы, которые не описаны схемой. При работе с такими платформами множество сценариев управления версиями легче реализовать.

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

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

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

Например, контракт службы обработки заказов на поставку с именем PoProcessing и операцией PostPurchaseOrder принимает параметр, соответствующий контракту данных PurchaseOrder. Если необходимо изменить контракт PurchaseOrder, следует создать новый контракт данных, т. е. PurchaseOrder2, с внесенными изменениями. Затем необходимо выполнить управление версиями на уровне контракта службы. Например, создайте операцию PostPurchaseOrder2, принимающую параметр PurchaseOrder2, или создайте контракт службы PoProcessing2, в котором операция PostPurchaseOrder принимает контракт данных PurchaseOrder2.

Обратите внимание, что изменения в контрактах данных, на которые ссылаются другие контракты данных, также расширяются до уровня модели службы. Например, в предыдущем сценарии контракт данных PurchaseOrder изменять не требуется. Однако он содержит член данных контракта данных Customer, который, в свою очередь, содержал член данных контракта данных Address, который не требуется изменять. В этом случае необходимо создать контракт данных Address2 с требуемыми изменениями, контракт данных Customer2, содержащий член данных Address2, и контракт данных PurchaseOrder2, содержащий член данных Customer2. Как и в предыдущем случае, также следует осуществить управление версиями контракта службы.

Несмотря на то что в этих примерах имена изменены (добавлена цифра "2"), рекомендуется изменять пространства имен вместо имен путем добавления новых пространств имен с номером или датой версии. Например, контракт данных http://schemas.contoso.com/2005/05/21/PurchaseOrder изменится на контракт данных http://schemas.contoso.com/2005/10/14/PurchaseOrder.

Дополнительные сведения см. в статье "Рекомендации по управлению версиями служб".

Иногда необходимо обеспечить строгое соответствие схеме для сообщений, отправляемых приложением, но нельзя полагаться на то, что входящие сообщения строго соответствуют схеме. В это случае существует опасность того, что входящее сообщение может содержать лишние данные. Лишние значения хранятся и возвращаются WCF, что приводит к отправке недопустимых сообщений схемы. Чтобы избежать этой проблемы, необходимо выключить возможность полной совместимости версий. Это можно сделать двумя способами.

Дополнительные сведения о циклического обхода см. в разделе "Контракты данных, совместимые с пересылкой".

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

Строгое соответствие схеме требуется редко. Множество платформ допускают наличие дополнительных элементов, не описанных в схеме. Пока это не допускается, можно использовать полный набор функций, описанных в разделе "Управление версиями контрактов данных" и "Контракты данных, совместимые с пересылкой". Рекомендуется соблюдать следующее.

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

  1. Не пытайтесь управлять версиями контрактов данных с использованием наследования типов. Чтобы создать более поздние версии, либо измените контракт данных в существуем типе, либо создайте новый несвязанный тип.

  2. Использование наследования вместе с контрактами данных допускается, если наследование не используется как механизм управления версиями и выполняются определенные правила. Если тип наследуется от определенного базового типа, он не должен наследоваться от другого базового типа в будущей версии (за исключением случаев, когда у них одинаковые контракты данных). Существует одно исключение из этого: можно вставить тип в иерархию между типом контракта данных и его базовым типом, но только если в нем не содержится членов данных с такими же именами, как у других членов в любых возможных версиях других типов в иерархии. Как правило, использование членов данных с одинаковыми именами на разных уровнях одной и той же иерархии наследования может привести к серьезным проблемам с управлением версиями. Этого следует избегать.

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

  4. В более поздних версиях не изменяйте имя или пространство имен контракта данных. При изменении имени или пространства имен базового типа контракта данных обязательно сохраните имя и пространство имен контракта данных с помощью соответствующих механизмов, таких как свойство Name атрибута DataContractAttribute. Дополнительные сведения об именовании см. в разделе "Имена контрактов данных".

  5. В более поздних версиях не изменяйте имена членов данных. При изменении имени поля, свойства или базового события члена данных используйте свойство Name атрибута DataMemberAttribute, чтобы сохранить существующее имя члена данных.

  6. В более поздних версиях не изменяйте тип поля, свойства или базового события члена данных, чтобы не изменился итоговый контракт данных для члена данных. Необходимо помнить, что типы интерфейсов эквивалентны классу Object, при определении ожидаемого контракта данных.

  7. В более поздних версиях не изменяйте порядок существующих членов данных путем изменения свойства Order атрибута DataMemberAttribute.

  8. В более поздних версиях можно добавлять новые члены данных. Они всегда должны соответствовать следующим правилам.

    1. Для свойства IsRequired всегда следует сохранять значение по умолчанию false.

    2. Если значение по умолчанию null или нуль для члена неприемлемо, то необходимо предусмотреть метод обратного вызова с использованием OnDeserializingAttribute для обеспечения разумного значения по умолчанию при отсутствии члена во входящем потоке. Дополнительные сведения о обратном вызове см. в разделе "Обратные вызовы" в версии Tolerant Serialization.

    3. Свойство DataMemberAttribute.Order должно использоваться, чтобы убедиться, что все добавленные элементы данных отображаются после существующих элементов данных. Рекомендуемый способ выполнения этого заключается в следующем: значение свойству Order не должно быть присвоено ни для одного члена данных в первой версии контракта данных. Для всех элементов данных, добавленных в версии 2 контракта данных, свойство Order должно иметь значение 2. Для всех элементов данных, добавленных в версии 3 контракта данных, свойство Order должно иметь значение 3 и так далее Допускается задание нескольких членов данных одному номеру свойства Order.

  9. Не удаляйте члены данных в более поздних версиях, даже если для свойства IsRequired сохранено значение по умолчанию false в предыдущих версиях.

  10. Не изменяйте свойство IsRequired существующих членов данных от версии к версии.

  11. Для требуемых членов данных (свойству IsRequired присвоено значение true) не изменяйте свойство EmitDefaultValue от версии к версии.

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

    Например, если в версии 1 контракта данных "Person" содержится только член данных "Name", не следует создавать версию 2a контракта, добавляя только член "Age", и версию 2b, добавляя только член "Address". Переход от версии 2a к версии 2b будет включать в себя удаление члена "Age" и добавление члена "Address". Переход в другом направлении повлечет за собой удаление члена "Address" и добавление члена"Age". Согласно этим правилам, удалять члены запрещается.

  13. Обычно не следует создавать новые подтипы существующих типов контрактов данных в новой версии приложения. Аналогично, не следует создавать новые контракты данных, используемые вместо членов данных, объявленных как "Object" или как типы интерфейса. Создание таких новых классов допускается, только если известно, что можно добавить новые типы в список известных типов всех экземпляров старого приложения. Например, в версии 1 приложения имеется тип контракта данных "LibraryItem" с подтипами контракта данных "Book" и "Newspaper". Тип "LibraryItem" будет иметь список известных типов, содержащий подтипы "Book" и "Newspaper". Предположим, в версию 2 добавляется тип "Magazine", являющийся подтипом типа "LibraryItem". При отправке экземпляра "Magazine" от версии 2 к версии 1 контракт данных "Magazine" невозможно найти в списке известных типов, и вызывается исключение.

  14. Не следует добавлять или удалять члены перечисления от версии к версии. Также не следует переименовывать члены перечисления, если не используется свойство "Name" атрибута EnumMemberAttribute, чтобы сохранить имена в модели контракта данных неизменными.

  15. Коллекции взаимозаменяемы в модели контракта данных, как описано в типах коллекций в контрактах данных. Это повышает степень гибкости. Однако убедитесь, что тип коллекции случайно не изменен невзаимоизменяемым способом от версии к версии. Например, не изменяйте ненастроенную коллекцию (т. е. без атрибута CollectionDataContractAttribute) на настроенную или настроенную коллекцию на ненастроенную. Кроме того, не изменяйте свойства атрибута CollectionDataContractAttribute от версии к версии. Единственным допустимым изменением является добавление свойства "Name" или "Namespace", если имя или пространство имен типа базовой коллекции были изменены, и необходимо, чтобы имя и пространство имен контракта данных были теми же, что и в предыдущей версии.

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

См. также