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


Лучшие практики: Версионирование контракта данных

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

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

При обсуждении управления версиями контрактов данных важно отметить, что схема контракта данных, экспортируемая 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, что приводит к тому, что отправляются сообщения, недопустимые согласно схеме. Чтобы избежать этой проблемы, следует отключить функцию переноса данных. Это можно сделать двумя способами.

Дополнительные сведения о круговом преобразовании см. в Forward-Compatible Контракты данных.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  11. Для обязательных элементов данных (где IsRequired равен true), не изменяйте свойство EmitDefaultValue от версии к версии.

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

    Например, если версия 1 контракта данных "Персона" содержит только элемент данных "Имя", то не следует создавать версию 2a контракта, добавляя только элемент "Возраст", и версию 2b, добавляя только элемент "Адрес". Переход с 2a до 2b будет включать удаление возраста и добавление адреса; идти в другом направлении будет влечет за собой удаление адреса и добавление возраста. Удаление участников не допускается этими рекомендациями.

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

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

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

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

См. также