Udostępnij za pośrednictwem


Przechowywanie wersji usługi

Po początkowym wdrożeniu i potencjalnie kilkukrotnie w okresie ich istnienia usługi (i udostępniane przez nie punkty końcowe) mogą wymagać zmiany z różnych powodów, takich jak zmiana potrzeb biznesowych, wymagania dotyczące technologii informatycznych lub rozwiązanie innych problemów. Każda zmiana wprowadza nową wersję usługi. W tym temacie wyjaśniono, jak rozważyć przechowywanie wersji w programie Windows Communication Foundation (WCF).

Cztery kategorie zmian usługi

Zmiany usług, które mogą być wymagane, można sklasyfikować w czterech kategoriach:

  • Zmiany kontraktu: na przykład można dodać operację lub dodać lub zmienić element danych w komunikacie.

  • Zmiany adresów: na przykład usługa przenosi się do innej lokalizacji, w której punkty końcowe mają nowe adresy.

  • Zmiany powiązań: na przykład zmiany mechanizmu zabezpieczeń lub zmiany jego ustawień.

  • Zmiany implementacji: na przykład w przypadku zmiany implementacji metody wewnętrznej.

Niektóre z tych zmian są nazywane "łamaniem", a inne są "nonbreaking". Zmiana jest niezgodna , jeśli wszystkie komunikaty, które zostałyby pomyślnie przetworzone w poprzedniej wersji, zostaną pomyślnie przetworzone w nowej wersji. Każda zmiana, która nie spełnia tego kryterium, jest zmianą powodującą niezgodność .

Orientacja usługi i przechowywanie wersji

Jednym z zestawów orientacji usługi jest to, że usługi i klienci są autonomiczne (lub niezależne). Między innymi oznacza to, że deweloperzy usług nie mogą zakładać, że kontrolują, a nawet wiedzą o wszystkich klientach usług. Eliminuje to możliwość ponownego kompilowania i ponownego wdrażania wszystkich klientów, gdy usługa zmienia wersje. W tym temacie założono, że usługa jest zgodna z tym zestawem i dlatego musi zostać zmieniona lub "wersjonowana" niezależnie od jej klientów.

W przypadkach, gdy zmiana powodująca niezgodność jest nieoczekiwana i nie można jej uniknąć, aplikacja może zignorować ten zestaw i wymagać ponownego skompilowania i ponownego wdrożenia klientów przy użyciu nowej wersji usługi.

Przechowywanie wersji kontraktu

Kontrakty używane przez klienta nie muszą być takie same jak kontrakt używany przez usługę; muszą być zgodne.

W przypadku kontraktów usług zgodność oznacza, że można dodać nowe operacje uwidocznione przez usługę, ale istniejących operacji nie można usunąć ani zmienić semantycznie.

W przypadku kontraktów danych zgodność oznacza, że można dodać nowe definicje typów schematu, ale istniejące definicje typów schematu nie mogą być zmieniane w sposób powodujący niezgodność. Zmiany powodujące niezgodność mogą obejmować usuwanie elementów członkowskich danych lub zmianę ich typu danych w sposób nieprzestrzenny. Ta funkcja umożliwia usłudze zmianę wersji kontraktów w niektórych szerokościach geograficznych bez przerywania pracy klientów. W kolejnych dwóch sekcjach opisano przełomowe i powodujące niezgodność zmiany, które można wprowadzić w kontraktach dotyczących danych i usług WCF.

Przechowywanie wersji kontraktów danych

Ta sekcja dotyczy przechowywania wersji danych podczas korzystania z DataContractSerializer klas i DataContractAttribute .

Ścisłe przechowywanie wersji

W wielu scenariuszach, gdy zmiana wersji jest problemem, deweloper usługi nie ma kontroli nad klientami i w związku z tym nie może podjąć założeń dotyczących sposobu reagowania na zmiany w pliku XML lub schema komunikatu. W takich przypadkach należy zagwarantować, że nowe komunikaty będą weryfikowane względem starego schematu z dwóch powodów:

  • Starzy klienci opracowali przy założeniu, że schemat nie ulegnie zmianie. Mogą one nie przetwarzać komunikatów, których nigdy nie zaprojektowano.

  • Starzy klienci mogą przeprowadzić rzeczywistą walidację schematu względem starego schematu, zanim jeszcze spróbują przetworzyć komunikaty.

Zalecane podejście w takich scenariuszach polega na traktowaniu istniejących kontraktów danych jako niezmiennych i tworzeniu nowych z unikatowymi kwalifikowanymi nazwami XML. Następnie deweloper usługi doda nowe metody do istniejącego kontraktu usługi lub utworzy nowy kontrakt usługi przy użyciu metod korzystających z nowego kontraktu danych.

Często zdarza się, że deweloper usługi musi napisać logikę biznesową, która powinna być uruchamiana we wszystkich wersjach kontraktu danych oraz kodu biznesowego specyficznego dla wersji dla każdej wersji kontraktu danych. Dodatek na końcu tego tematu wyjaśnia, w jaki sposób interfejsy mogą być używane do zaspokojenia tych potrzeb.

Przechowywanie wersji lax

W wielu innych scenariuszach deweloper usługi może założyć, że dodanie nowego, opcjonalnego elementu członkowskiego do kontraktu danych nie spowoduje przerwania istniejących klientów. Wymaga to od dewelopera usługi zbadania, czy istniejący klienci nie przeprowadzają weryfikacji schematu i czy ignorują nieznane elementy członkowskie danych. W tych scenariuszach można korzystać z funkcji kontraktu danych na potrzeby dodawania nowych członków w sposób niezwiązany. Deweloper usługi może podjąć to założenie z pewnością, jeśli funkcje kontraktu danych na potrzeby przechowywania wersji były już używane w pierwszej wersji usługi.

WCF, ASP.NET Web Services i wiele innych stosów usług sieci Web obsługuje przechowywanie wersji lax: oznacza to, że nie zgłaszają wyjątków dla nowych nieznanych elementów członkowskich danych w odebranych danych.

Łatwo jest błędnie sądzić, że dodanie nowego elementu członkowskiego nie spowoduje przerwania istniejących klientów. Jeśli nie masz pewności, że wszyscy klienci mogą obsługiwać niezręczne przechowywanie wersji, zaleca się stosowanie rygorystycznych wytycznych dotyczących przechowywania wersji i traktowanie kontraktów danych jako niezmiennych.

Aby uzyskać szczegółowe wytyczne dotyczące zarówno nieczystego, jak i ścisłego przechowywania wersji kontraktów danych, zobacz Best Practices: Data Contract Versioning (Najlepsze rozwiązania: przechowywanie wersji kontraktu danych).

Rozróżnianie między typami kontraktu danych i platformy .NET

Klasę lub strukturę platformy .NET można projektować jako kontrakt danych, stosując DataContractAttribute atrybut do klasy. Typ platformy .NET i projekcje kontraktu danych to dwie odrębne kwestie. Istnieje możliwość posiadania wielu typów platformy .NET z tą samą projekcją kontraktu danych. To rozróżnienie jest szczególnie przydatne w przypadku umożliwienia zmiany typu platformy .NET przy zachowaniu przewidywanego kontraktu danych, zapewniając zgodność z istniejącymi klientami nawet w ścisłym znaczeniu słowa. Istnieją dwie czynności, które należy zawsze wykonać, aby zachować to rozróżnienie między typem platformy .NET a kontraktem danych:

  • Określ element i NameNamespace. Zawsze należy określić nazwę i przestrzeń nazw kontraktu danych, aby uniemożliwić uwidocznienie nazwy i przestrzeni nazw typu .NET w kontrakcie. W ten sposób, jeśli zdecydujesz się później zmienić przestrzeń nazw lub nazwę typu platformy .NET, kontrakt danych pozostaje taki sam.

  • Podaj wartość Name. Zawsze należy określić nazwę członków danych, aby uniemożliwić uwidocznienie nazwy członka platformy .NET w umowie. W ten sposób, jeśli zdecydujesz się później zmienić nazwę członka platformy .NET, kontrakt danych pozostaje taki sam.

Zmienianie lub usuwanie członków

Zmiana nazwy lub typu danych elementu członkowskiego lub usunięcie elementów członkowskich danych jest zmianą powodującą niezgodność, nawet jeśli przechowywanie wersji nieczystej jest dozwolone. Jeśli jest to konieczne, utwórz nowy kontrakt danych.

Jeśli zgodność usługi ma duże znaczenie, możesz rozważyć zignorowanie nieużywanych elementów członkowskich danych w kodzie i pozostawienie ich na miejscu. Jeśli dzielisz element członkowski danych na wiele elementów członkowskich, możesz rozważyć pozostawienie istniejącego elementu członkowskiego w miejscu jako właściwości, która może wykonać wymagane dzielenie i ponowne agregowanie dla klientów na poziomie podrzędnym (klientów, którzy nie są uaktualnieni do najnowszej wersji).

Podobnie zmiany nazwy lub przestrzeni nazw kontraktu danych są zmianami powodujących niezgodność.

Zaokrąglenia nieznanych danych

W niektórych scenariuszach istnieje potrzeba "round-trip" nieznanych danych, które pochodzą z członków dodanych w nowej wersji. Na przykład usługa "versionNew" wysyła dane z niektórymi nowo dodanymi członkami do klienta "versionOld". Klient ignoruje nowo dodanych członków podczas przetwarzania komunikatu, ale ponownie wysyła te same dane, w tym nowo dodanych członków, z powrotem do wersjiNowa usługa. Typowy scenariusz dotyczy aktualizacji danych, w których dane są pobierane z usługi, zmieniane i zwracane.

Aby włączyć round-tripping dla określonego typu, typ musi zaimplementować IExtensibleDataObject interfejs. Interfejs zawiera jedną właściwość, ExtensionData która zwraca ExtensionDataObject typ. Właściwość jest używana do przechowywania danych z przyszłych wersji kontraktu danych, który jest nieznany bieżącej wersji. Te dane są nieprzezroczyste dla klienta, ale gdy wystąpienie jest serializowane, zawartość ExtensionData właściwości jest zapisywana przy użyciu pozostałych danych składowych kontraktu danych.

Zaleca się zaimplementowanie tego interfejsu przez wszystkie typy w celu uwzględnienia nowych i nieznanych przyszłych elementów członkowskich.

Biblioteki kontraktów danych

Mogą istnieć biblioteki kontraktów danych, w których kontrakt jest publikowany w centralnym repozytorium, a implementatory usług i typów implementują i uwidaczniają kontrakty danych z tego repozytorium. W takim przypadku podczas publikowania kontraktu danych w repozytorium nie masz kontroli nad tym, kto tworzy typy, które je implementują. W związku z tym nie można zmodyfikować kontraktu po jego opublikowaniu, co skutecznie nie jest niezmienne.

W przypadku korzystania z narzędzia XmlSerializer

Te same zasady przechowywania wersji mają zastosowanie podczas korzystania z XmlSerializer klasy . Jeśli wymagane jest ścisłe przechowywanie wersji, należy traktować kontrakty danych jako niezmienne i tworzyć nowe kontrakty danych z unikatowymi, kwalifikowanymi nazwami nowych wersji. Jeśli masz pewność, że można użyć obsługi wersji z opóźnieniem, możesz dodać nowe elementy członkowskie z możliwością serializacji w nowych wersjach, ale nie zmieniać ani usuwać istniejących elementów członkowskich.

Uwaga

XmlAnyElementAttribute Używa XmlSerializer atrybutów i XmlAnyAttributeAttribute do obsługi zaokrąglania nieznanych danych.

Przechowywanie wersji kontraktu komunikatów

Wytyczne dotyczące przechowywania wersji kontraktów komunikatów są bardzo podobne do wersji kontraktów danych. Jeśli wymagane jest ścisłe przechowywanie wersji, nie należy zmieniać treści wiadomości, ale zamiast tego utworzyć nowy kontrakt komunikatu z unikatową kwalifikowaną nazwą. Jeśli wiesz, że możesz użyć obsługi wersji lax, możesz dodać nowe części treści komunikatów, ale nie zmieniać ani usuwać istniejących. Te wskazówki dotyczą zarówno gołych, jak i opakowanych kontraktów komunikatów.

Nagłówki komunikatów można zawsze dodawać, nawet jeśli jest używana ścisła obsługa wersji. Flaga MustUnderstand może mieć wpływ na przechowywanie wersji. Ogólnie rzecz biorąc, model przechowywania wersji nagłówków w usłudze WCF jest zgodnie ze specyfikacją protokołu SOAP.

Przechowywanie wersji kontraktu usługi

Podobnie jak w przypadku przechowywania wersji kontraktu danych, przechowywanie wersji kontraktu usługi obejmuje również dodawanie, zmienianie i usuwanie operacji.

Określanie nazwy, przestrzeni nazw i akcji

Domyślnie nazwa kontraktu usługi to nazwa interfejsu. Domyślną przestrzenią nazw jest http://tempuri.org, a akcja każdej operacji to http://tempuri.org/contractname/methodname. Zaleca się jawne określenie nazwy i przestrzeni nazw dla kontraktu usługi oraz akcji dla każdej operacji, aby uniknąć używania http://tempuri.org i zapobiegania uwidacznianiu nazw interfejsów i metod w umowie usługi.

Dodawanie parametrów i operacji

Dodawanie operacji usługi uwidacznianych przez usługę jest zmianą niełamającą się, ponieważ istniejący klienci nie muszą być zaniepokojeni tymi nowymi operacjami.

Uwaga

Dodawanie operacji do kontraktu wywołania zwrotnego dwukierunkowego jest zmianą powodującą niezgodność.

Zmiana parametru operacji lub zwracanych typów

Zmiana typów parametrów lub zwracanych zazwyczaj jest zmianą powodującą niezgodność, chyba że nowy typ implementuje ten sam kontrakt danych implementowany przez stary typ. Aby wprowadzić taką zmianę, dodaj nową operację do kontraktu usługi lub zdefiniuj nowy kontrakt usługi.

Usuwanie operacji

Usuwanie operacji jest również zmianą powodującą niezgodność. Aby wprowadzić taką zmianę, zdefiniuj nowy kontrakt usługi i uwidocznij go w nowym punkcie końcowym.

Kontrakty błędów

Atrybut FaultContractAttribute umożliwia deweloperowi kontraktu usługi określenie informacji o błędach, które można zwrócić z operacji kontraktu.

Lista błędów opisanych w umowie usługi nie jest uznawana za wyczerpującą. W dowolnym momencie operacja może zwracać błędy, które nie zostały opisane w umowie. W związku z tym zmiana zestawu błędów opisanych w umowie nie jest uznawana za niezgodną. Na przykład dodanie nowej błędu do kontraktu przy użyciu FaultContractAttribute lub usunięcie istniejącej błędu z kontraktu.

Biblioteki kontraktów usług

Organizacje mogą mieć biblioteki kontraktów, w których kontrakt jest publikowany w centralnym repozytorium i implementatorach usług implementujących kontrakty z tego repozytorium. W takim przypadku podczas publikowania kontraktu usługi w repozytorium nie masz kontroli nad tym, kto tworzy usługi, które je implementują. W związku z tym nie można zmodyfikować kontraktu usługi po opublikowaniu, co skutecznie nie jest niezmienne. Program WCF obsługuje dziedziczenie kontraktów, którego można użyć do utworzenia nowego kontraktu rozszerzającego istniejące kontrakty. Aby użyć tej funkcji, zdefiniuj nowy interfejs kontraktu usługi, który dziedziczy ze starego interfejsu kontraktu usługi, a następnie dodaj metody do nowego interfejsu. Następnie należy zmienić usługę, która implementuje stary kontrakt, aby zaimplementować nowy kontrakt i zmienić definicję punktu końcowego "versionOld" tak, aby korzystała z nowego kontraktu. W przypadku klientów "versionOld" punkt końcowy będzie nadal wyświetlany jako uwidacznianie kontraktu "versionOld"; w przypadku klientów "versionNew" punkt końcowy pojawi się, aby uwidocznić kontrakt "versionNew".

Przechowywanie wersji adresów i powiązań

Zmiany adresów i powiązań punktu końcowego są zmianami powodującymi niezgodność, chyba że klienci mogą dynamicznie odnajdywać nowy adres punktu końcowego lub powiązanie. Jednym z mechanizmów implementowania tej funkcji jest użycie rejestru uniwersalnego opisu odnajdywania i integracji (UDDI) oraz wzorca wywołania UDDI, w którym klient próbuje komunikować się z punktem końcowym i, po awarii, wysyła zapytanie do dobrze znanego rejestru UDDI dla bieżących metadanych punktu końcowego. Następnie klient używa adresu i powiązania z tych metadanych do komunikowania się z punktem końcowym. Jeśli ta komunikacja powiedzie się, klient buforuje adres i informacje o powiązaniu do użycia w przyszłości.

Usługa routingu i przechowywanie wersji

Jeśli zmiany wprowadzone w usłudze zmieniają się w sposób powodujący niezgodność i musisz mieć co najmniej dwie różne wersje usługi uruchomionej jednocześnie, możesz użyć usługi routingu WCF do kierowania komunikatów do odpowiedniego wystąpienia usługi. Usługa routingu WCF używa routingu opartego na zawartości, czyli używa informacji w komunikacie, aby określić, gdzie ma być kierowany komunikat. Aby uzyskać więcej informacji na temat usługi routingu WCF, zobacz Routing Service (Usługa routingu). Aby zapoznać się z przykładem korzystania z usługi routingu WCF na potrzeby przechowywania wersji usługi, zobacz How To: Service Versioning (Instrukcje: przechowywanie wersji usługi).

Dodatek

Ogólne wskazówki dotyczące przechowywania wersji kontraktu danych w przypadku konieczności ścisłego przechowywania wersji polega na traktowaniu kontraktów danych jako niezmiennych i tworzeniu nowych, gdy wymagane są zmiany. Dla każdego nowego kontraktu danych należy utworzyć nową klasę, więc potrzebny jest mechanizm, aby uniknąć konieczności uwzględnienia istniejącego kodu napisanego pod względem starej klasy kontraktu danych i przepisania jej pod względem nowej klasy kontraktu danych.

Jednym z takich mechanizmów jest użycie interfejsów do definiowania składowych każdego kontraktu danych i pisania wewnętrznego kodu implementacji pod względem interfejsów, a nie klas kontraktów danych, które implementują interfejsy. Poniższy kod dla wersji 1 usługi przedstawia IPurchaseOrderV1 interfejs i :PurchaseOrderV1

public interface IPurchaseOrderV1  
{  
    string OrderId { get; set; }  
    string CustomerId { get; set; }  
}  
  
[DataContract(  
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2005/10/PurchaseOrder")]  
public class PurchaseOrderV1 : IPurchaseOrderV1  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
}  

Chociaż operacje kontraktu PurchaseOrderV1usługi byłyby napisane w kategoriach , rzeczywista logika biznesowa byłaby w kategoriach IPurchaseOrderV1. Następnie w wersji 2 będzie nowy IPurchaseOrderV2 interfejs i nowa PurchaseOrderV2 klasa, jak pokazano w poniższym kodzie:

public interface IPurchaseOrderV2  
{  
    DateTime OrderDate { get; set; }  
}

[DataContract(
Name = "PurchaseOrder",  
Namespace = "http://examples.microsoft.com/WCF/2006/02/PurchaseOrder")]  
public class PurchaseOrderV2 : IPurchaseOrderV1, IPurchaseOrderV2  
{  
    [DataMember(...)]  
    public string OrderId {...}  
    [DataMember(...)]  
    public string CustomerId {...}  
    [DataMember(...)]  
    public DateTime OrderDate { ... }  
}  

Umowa usługi zostanie zaktualizowana w celu uwzględnienia nowych operacji napisanych pod względem .PurchaseOrderV2 Istniejąca logika biznesowa napisana pod względem IPurchaseOrderV1 będzie nadal działać dla PurchaseOrderV2 nowej logiki biznesowej, która wymaga OrderDate , aby właściwość została napisana pod względem IPurchaseOrderV2.

Zobacz też