Najlepsze rozwiązania: przechowywanie wersji kontraktów danych
W tym temacie wymieniono najlepsze rozwiązania dotyczące tworzenia kontraktów danych, które mogą łatwo ewoluować w miarę upływu czasu. Aby uzyskać więcej informacji na temat kontraktów danych, zobacz tematy w temacie Using Data Contracts (Korzystanie z kontraktów danych).
Uwaga dotycząca sprawdzania poprawności schematu
Omawiając przechowywanie wersji kontraktu danych, należy pamiętać, że schemat kontraktu danych wyeksportowany przez program Windows Communication Foundation (WCF) nie ma żadnej obsługi wersji, poza faktem, że elementy są domyślnie oznaczone jako opcjonalne.
Oznacza to, że nawet najbardziej typowy scenariusz przechowywania wersji, taki jak dodanie nowego elementu członkowskiego danych, nie może być zaimplementowany w sposób bezproblemowy w odniesieniu do danego schematu. Nowsze wersje kontraktu danych (na przykład z nowym elementem członkowskim danych) nie są weryfikowane przy użyciu starego schematu.
Istnieje jednak wiele scenariuszy, w których nie jest wymagana ścisła zgodność schematu. Wiele platform usług sieci Web, w tym usług sieci Web WCF i XML utworzonych przy użyciu ASP.NET, domyślnie nie przeprowadza weryfikacji schematu i dlatego toleruje dodatkowe elementy, które nie są opisane przez schemat. Podczas pracy z takimi platformami wiele scenariuszy przechowywania wersji jest łatwiejszych do zaimplementowania.
W związku z tym istnieją dwa zestawy wytycznych dotyczących przechowywania wersji kontraktów danych: jeden zestaw dla scenariuszy, w których ważna jest ścisła ważność schematu, a drugi zestaw dla scenariuszy, gdy nie jest.
Przechowywanie wersji, gdy wymagana jest weryfikacja schematu
Jeśli wymagana jest ścisła ważność schematu we wszystkich kierunkach (nowe i stare i stare), kontrakty danych powinny być uznawane za niezmienne. Jeśli wymagane jest przechowywanie wersji, należy utworzyć nowy kontrakt danych z inną nazwą lub przestrzenią nazw, a kontrakt usługi używający typu danych powinien być odpowiednio wersjonowany.
Na przykład kontrakt usługi przetwarzania zamówień zakupu o nazwie PoProcessing
z operacją przyjmuje parametr zgodny z PostPurchaseOrder
kontraktem PurchaseOrder
danych. PurchaseOrder
Jeśli kontrakt musi ulec zmianie, musisz utworzyć nowy kontrakt danych, PurchaseOrder2
czyli , który zawiera zmiany. Następnie należy obsługiwać przechowywanie wersji na poziomie kontraktu usługi. Na przykład przez utworzenie PostPurchaseOrder2
operacji, która przyjmuje PurchaseOrder2
parametr, lub przez utworzenie PoProcessing2
kontraktu usługi, w którym PostPurchaseOrder
operacja przyjmuje PurchaseOrder2
kontrakt danych.
Należy pamiętać, że zmiany kontraktów danych, do których odwołuje się inne kontrakty danych, również rozszerzają się na warstwę modelu usług. Na przykład w poprzednim scenariuszu PurchaseOrder
kontrakt danych nie musi się zmieniać. Zawiera jednak element członkowski danych kontraktu Customer
danych, który z kolei zawierał element członkowski kontraktu Address
danych, który musi zostać zmieniony. W takim przypadku należy utworzyć Address2
kontrakt danych z wymaganymi zmianami, Customer2
kontraktem danych zawierającym Address2
element członkowski danych i kontraktem danych zawierającym PurchaseOrder2
element członkowski Customer2
danych. Podobnie jak w poprzednim przypadku, umowa serwisowa musiałaby być również wersjonowana.
Chociaż w tych przykładach nazwy są zmieniane (dołączając znak "2"), zaleca się zmianę przestrzeni nazw zamiast nazw przez dołączenie nowych przestrzeni nazw z numerem wersji lub datą. Na przykład http://schemas.contoso.com/2005/05/21/PurchaseOrder
kontrakt danych zmieni się na http://schemas.contoso.com/2005/10/14/PurchaseOrder
kontrakt danych.
Aby uzyskać więcej informacji, zobacz Najlepsze rozwiązania: przechowywanie wersji usługi.
Czasami należy zagwarantować ścisłą zgodność schematu dla komunikatów wysyłanych przez aplikację, ale nie można polegać na przychodzących komunikatach, aby być ściśle zgodne ze schematem. W takim przypadku istnieje niebezpieczeństwo, że komunikat przychodzący może zawierać nadmiarowe dane. Nadmiarowe wartości są przechowywane i zwracane przez usługę WCF, co powoduje wysłanie nieprawidłowych komunikatów schematu. Aby uniknąć tego problemu, funkcja zaokrąglania powinna być wyłączona. Istnieją dwa sposoby, aby to zrobić.
Nie implementuj interfejsu IExtensibleDataObject na żadnym z typów.
ServiceBehaviorAttribute Zastosuj atrybut do kontraktu usługi z właściwością ustawioną IgnoreExtensionDataObject na
true
.
Aby uzyskać więcej informacji na temat zaokrąglania, zobacz Kontrakty danych zgodne z przekazywaniem.
Przechowywanie wersji, gdy weryfikacja schematu nie jest wymagana
Ścisła zgodność schematu jest rzadko wymagana. Wiele platform toleruje dodatkowe elementy, które nie są opisane przez schemat. O ile jest to tolerowane, można użyć pełnego zestawu funkcji opisanych w artykule Przechowywanie wersji kontraktów danych i kontrakty danych zgodne z przekazywaniem. Zalecane są następujące wskazówki.
Niektóre z wytycznych muszą być dokładnie zgodne, aby wysyłać nowe wersje typu, w których oczekuje się starszej wersji lub wysłać stary, w którym oczekuje się nowego. Inne wytyczne nie są ściśle wymagane, ale są wymienione tutaj, ponieważ mogą one mieć wpływ na przyszłość przechowywania wersji schematu.
Nie należy próbować wersjonować kontraktów danych według dziedziczenia typu. Aby utworzyć nowsze wersje, zmień kontrakt danych na istniejący typ lub utwórz nowy niepowiązany typ.
Korzystanie z dziedziczenia razem z kontraktami danych jest dozwolone, pod warunkiem, że dziedziczenie nie jest używane jako mechanizm przechowywania wersji i że są przestrzegane pewne reguły. Jeśli typ pochodzi z określonego typu podstawowego, nie należy go uzyskiwać z innego typu podstawowego w przyszłej wersji (chyba że ma ten sam kontrakt danych). Istnieje jeden wyjątek: można wstawić typ do hierarchii między typem kontraktu danych a jego typem podstawowym, ale tylko wtedy, gdy nie zawiera elementów członkowskich danych o takich samych nazwach jak inne elementy członkowskie w dowolnej możliwej wersji innych typów w hierarchii. Ogólnie rzecz biorąc, używanie elementów członkowskich danych o tych samych nazwach na różnych poziomach tej samej hierarchii dziedziczenia może prowadzić do poważnych problemów z przechowywaniem wersji i należy unikać.
Począwszy od pierwszej wersji kontraktu danych, zawsze zaimplementuj IExtensibleDataObject , aby włączyć round-tripping. Aby uzyskać więcej informacji, zobacz Kontrakty danych zgodne z przekazywaniem. Jeśli wydano co najmniej jedną wersję typu bez implementowania tego interfejsu, zaimplementuj go w następnej wersji typu.
W nowszych wersjach nie zmieniaj nazwy kontraktu danych ani przestrzeni nazw. W przypadku zmiany nazwy lub przestrzeni nazw typu bazowego kontraktu danych należy zachować nazwę kontraktu danych i przestrzeń nazw przy użyciu odpowiednich mechanizmów, takich jak Name właściwość DataContractAttribute. Aby uzyskać więcej informacji na temat nazewnictwa, zobacz Nazwy kontraktów danych.
W nowszych wersjach nie zmieniaj nazw żadnych elementów członkowskich danych. W przypadku zmiany nazwy pola, właściwości lub zdarzenia bazowego elementu członkowskiego danych użyj
Name
właściwości DataMemberAttribute , aby zachować istniejącą nazwę elementu członkowskiego danych.W nowszych wersjach nie zmieniaj typu żadnego pola, właściwości lub zdarzenia bazowego elementu członkowskiego danych, tak aby wynikowa umowa danych dla tego elementu członkowskiego danych uległa zmianie. Należy pamiętać, że typy interfejsów są równoważne Object do celów określania oczekiwanego kontraktu danych.
W nowszych wersjach nie zmieniaj kolejności istniejących elementów członkowskich danych, dostosowując Order właściwość atrybutu DataMemberAttribute .
W nowszych wersjach można dodać nowe elementy członkowskie danych. Powinny one zawsze przestrzegać następujących reguł:
Właściwość IsRequired powinna zawsze być pozostawiona na wartości domyślnej
false
.Jeśli wartość
null
domyślna lub zero dla elementu członkowskiego jest niedopuszczalna, należy podać metodę wywołania zwrotnego przy użyciu OnDeserializingAttribute elementu , aby zapewnić rozsądną wartość domyślną w przypadku, gdy element członkowski nie jest obecny w strumieniu przychodzącym. Aby uzyskać więcej informacji na temat wywołania zwrotnego, zobacz Wywołania zwrotne serializacji odporne na wersje.Właściwość powinna służyć do upewnienia DataMemberAttribute.Order się, że wszystkie nowo dodane elementy członkowskie danych są wyświetlane po istniejących elementach członkowskich danych. Zalecanym sposobem wykonania tej czynności jest następująca: Żaden z elementów członkowskich danych w pierwszej wersji kontraktu danych nie powinien mieć zestawu
Order
właściwości. Wszyscy członkowie danych dodani w wersji 2 kontraktu danych powinni mieć właściwośćOrder
ustawioną na 2. Wszyscy członkowie danych dodani w wersji 3 kontraktu danych powinni mieć ustawionąOrder
wartość 3 itd. Dopuszczalne jest posiadanie więcej niż jednego elementu członkowskiego danych ustawionego na tę samąOrder
liczbę.
Nie usuwaj elementów członkowskich danych w nowszych wersjach, nawet jeśli IsRequired właściwość została pozostawiona we właściwości domyślnej w poprzednich
false
wersjach.Nie zmieniaj
IsRequired
właściwości istniejących elementów członkowskich danych z wersji na wersję.W przypadku wymaganych elementów członkowskich danych (gdzie
IsRequired
totrue
), nie należy zmieniaćEmitDefaultValue
właściwości z wersji na wersję.Nie należy próbować tworzyć rozgałęzionych hierarchii przechowywania wersji. Oznacza to, że zawsze powinna istnieć ścieżka w co najmniej jednym kierunku od dowolnej wersji do innej wersji przy użyciu tylko zmian dozwolonych przez te wytyczne.
Jeśli na przykład wersja 1 kontraktu danych osoby zawiera tylko element członkowski danych Name, nie należy tworzyć wersji 2a kontraktu dodającego tylko element członkowski wieku i wersję 2b, dodając tylko element członkowski Adresu. Przejście z zakresu od 2a do 2b wiązałoby się z usunięciem wieku i dodaniem adresu; przejście w innym kierunku wiązałoby się z usunięciem adresu i dodaniem wieku. Usunięcie elementów członkowskich nie jest dozwolone przez te wytyczne.
Zazwyczaj nie należy tworzyć nowych podtypów istniejących typów kontraktów danych w nowej wersji aplikacji. Podobnie nie należy tworzyć nowych kontraktów danych, które są używane zamiast elementów członkowskich danych zadeklarowanych jako Obiekt lub jako typy interfejsów. Tworzenie tych nowych klas jest dozwolone tylko wtedy, gdy wiesz, że można dodać nowe typy do znanej listy typów wszystkich wystąpień starej aplikacji. Na przykład w wersji 1 aplikacji może istnieć typ kontraktu danych LibraryItem z podtypami kontraktu książki i gazety. BibliotekaItem będzie mieć znaną listę typów zawierającą książkę i gazetę. Załóżmy, że teraz dodasz typ magazynu w wersji 2, która jest podtypem BibliotekiItem. Jeśli wyślesz wystąpienie magazynu z wersji 2 do wersji 1, umowa danych magazynu nie zostanie znaleziona na liście znanych typów i zostanie zgłoszony wyjątek.
Nie należy dodawać ani usuwać elementów członkowskich wyliczenia między wersjami. Nie należy również zmieniać nazwy elementów członkowskich wyliczenia, chyba że używasz właściwości Name atrybutu
EnumMemberAttribute
, aby zachować ich nazwy w modelu kontraktu danych tak samo.Kolekcje są wymienne w modelu kontraktu danych zgodnie z opisem w temacie Typy kolekcji w kontraktach danych. Zapewnia to dużą elastyczność. Upewnij się jednak, że nie przypadkowo zmieniasz typu kolekcji w sposób niezamienny z wersji na wersję. Na przykład nie należy zmieniać kolekcji niestandardowej (bez atrybutu
CollectionDataContractAttribute
) na niestandardową lub dostosowaną kolekcję do kolekcji, która nie jest niestandardowa. Ponadto nie należy zmieniać właściwości wCollectionDataContractAttribute
wersji z na wersję. Jedyną dozwoloną zmianą jest dodanie właściwości Nazwa lub Przestrzeń nazw, jeśli nazwa lub przestrzeń nazw bazowego typu kolekcji uległa zmianie i musisz ustawić jej nazwę kontraktu danych i przestrzeń nazw tak samo jak w poprzedniej wersji.
Niektóre z wymienionych tutaj wytycznych można bezpiecznie zignorować, gdy mają zastosowanie specjalne okoliczności. Przed odejmowaniem od wytycznych upewnij się, że w pełni rozumiesz mechanizmy serializacji, deserializacji i schematu.
Zobacz też
- Name
- DataContractAttribute
- Order
- IsRequired
- IExtensibleDataObject
- ServiceBehaviorAttribute
- ExtensionData
- ExtensionDataObject
- OnDeserializingAttribute
- Używanie kontraktów danych
- Przechowywanie wersji kontraktów danych
- Nazwy kontraktów danych
- Kontrakty danych zgodne z nowszymi wersjami
- Wywołania zwrotne serializacji z tolerancją dla wersji