Teilen über


Datenvertragsversionierung

Während sich Anwendungen entwickeln, müssen Sie möglicherweise auch die Datenverträge ändern, die die Dienste verwenden. In diesem Thema wird erläutert, wie Datenverträge versioniert werden. In diesem Thema werden die Mechanismen für die Datenvertragsversionsverwaltung beschrieben. Eine vollständige Übersicht und anleitung zur präskriptiven Versionsverwaltung finden Sie unter Best Practices: Data Contract Versionsing.

Breaking Changes gegenüber Non-Breaking Changes

Änderungen an einem Datenvertrag können "breaking" oder "non-breaking" sein. Wenn ein Datenvertrag in nicht breaking Weise geändert wird, kann eine Anwendung, die die ältere Version des Vertrags verwendet, mit einer Anwendung, die die neuere Version verwendet, kommunizieren, und umgekehrt kann eine Anwendung mit der neueren Version des Vertrags mit einer Anwendung, die die ältere Version verwendet, kommunizieren. Andererseits verhindert eine bahnbrechende Änderung die Kommunikation in einer oder beiden Richtungen.

Jede Änderung eines Typs, der nicht beeinflusst, wie er gesendet und empfangen wird, ist "non-breaking". Solche Änderungen ändern nicht den Datenvertrag, nur den zugrunde liegenden Typ. Sie können z. B. den Namen eines Felds ohne Unterbrechung ändern, wenn Sie dann die Eigenschaft von Name auf den Namen der älteren Version von DataMemberAttribute festlegen. Der folgende Code zeigt Version 1 eines Datenvertrags.

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

Im folgenden Code wird ein "Non-Breaking Change" veranschaulicht.

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

Einige Änderungen ändern die gesendeten Daten, müssen aber nicht unbedingt "breaking" sein. Die folgenden Änderungen sind immer "breaking":

  • Ändern des Name- oder Namespace-Werts eines Datenvertrags.

  • Das Ändern der Reihenfolge der Datenelemente über die Eigenschaft Order der Klasse DataMemberAttribute.

  • Umbenennen eines Datenelements.

  • Ändern des Datenvertrags eines Datenmitglieds. Ändern Sie z. B. den Typ des Datenelements von einer ganzen Zahl in eine Zeichenfolge oder von einem Typ mit einem Datenvertrag namens "Kunde" in einen Typ mit dem Namen "Person".

Die folgenden Änderungen sind ebenfalls möglich.

Hinzufügen und Entfernen von Datenelementen

In den meisten Fällen ist das Hinzufügen oder Entfernen eines Datenelements keine wesentliche Änderung, es sei denn, Sie benötigen eine strenge Schemagültigkeit (neue Instanzen, die auf das alte Schema überprüft werden).

Wenn ein Typ mit einem zusätzlichen Feld in einen Typ mit einem fehlenden Feld deserialisiert wird, werden die zusätzlichen Informationen ignoriert. (Es kann auch für Rundreisezwecke gespeichert werden; weitere Informationen finden Sie unter Forward-Compatible Datenverträge).

Wenn ein Typ mit einem fehlenden Feld in einen Typ mit einem zusätzlichen Feld deserialisiert wird, bleibt das zusätzliche Feld auf seinen Standardwert gesetzt, in der Regel Null oder null. (Der Standardwert kann geändert werden; weitere Informationen finden Sie unterVersion-Tolerant Serialisierungsrückrufe.)

Sie können z. B. die CarV1 Klasse auf einem Client und die CarV2 Klasse auf einem Dienst verwenden, oder Sie können die CarV1 Klasse auf einem Dienst und die CarV2 Klasse auf einem Client verwenden.

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

Der Endpunkt der Version 2 kann Daten erfolgreich an den Endpunkt der Version 1 senden. Die Serialisierung von Version 2 des Car Datenvertrags liefert XML ähnlich wie im folgenden.

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

Die Deserialisierungs-Engine auf V1 findet kein entsprechendes Datenelement für das Feld HorsePower und verwirft die Daten.

Außerdem kann der Endpunkt der Version 1 Daten an den Endpunkt der Version 2 senden. Die Serialisierung von Version 1 des Car Datenvertrags liefert XML ähnlich wie im folgenden.

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

Der Deserializer der Version 2 weiß nicht, auf was das HorsePower Feld festgelegt werden soll, da keine übereinstimmenden Daten in der eingehenden XML vorhanden sind. Stattdessen wird das Feld auf den Standardwert 0 festgelegt.

Erforderliche Datenelemente

Ein Datenelement kann als "erforderlich" markiert werden, indem die Eigenschaft IsRequired der Klasse DataMemberAttribute auf true festgelegt wird. Wenn erforderliche Daten beim Deserialisieren fehlen, wird eine Ausnahme ausgelöst, anstatt das Datenelement auf den Standardwert festzulegen.

Das Hinzufügen eines erforderlichen Datenelements ist eine bedeutende Änderung. Das heißt, der neuere Typ kann weiterhin an Endpunkte mit dem älteren Typ gesendet werden, aber nicht umgekehrt. Das Entfernen eines Datenelements, das in einer früheren Version als erforderlich gekennzeichnet wurde, ist auch eine bedeutende Änderung.

Die Änderung des Eigenschaftswerts IsRequired von true nach false ist nicht "breaking", aber die Änderung von false nach true kann "breaking" sein, wenn eine frühere Version des Typs das fragliche Datenelement nicht besitzt.

Hinweis

Obwohl die IsRequired Eigenschaft auf true festgelegt ist, sind die eingehenden Daten möglicherweise null oder null, und ein Typ muss darauf vorbereitet sein, diese Möglichkeit zu behandeln. Verwenden Sie IsRequired nicht als Sicherheitsmechanismus, um vor schlechten eingehenden Daten zu schützen.

Ausgelassene Standardwerte

Es ist möglich (obwohl nicht empfohlen), die EmitDefaultValue Eigenschaft für das DataMemberAttribute-Attribut auf festzulegen false, wie in Data Member Default Values beschrieben. Wenn diese Einstellung festgelegt ist false, wird das Datenelement nicht ausgegeben, wenn es auf den Standardwert festgelegt ist (in der Regel NULL oder Null). Dies ist nicht mit den erforderlichen Datenmitgliedern in unterschiedlichen Versionen auf zwei Arten kompatibel:

  • Ein Datenvertrag mit einem Datenelement, das in einer Version erforderlich ist, kann keine Standarddaten (NULL oder 0(null)) von einer anderen Version erhalten, in der das Datenelement EmitDefaultValue auf false festgelegt hat.

  • Ein erforderliches Datenmitglied, bei dem EmitDefaultValue auf false gesetzt ist, kann nicht verwendet werden, um seinen Standardwert (null oder null) zu serialisieren, aber es kann einen solchen Wert bei der Deserialisierung erhalten. Dies schafft ein Round-Tripping-Problem (Daten können eingelesen werden, aber die gleichen Daten können nicht ausgegeben werden). Daher sollte, wenn IsRequired in einer Version true ist und EmitDefaultValuefalse ist, die gleiche Kombination für alle anderen Versionen gelten, sodass keine Version des Datenvertrags einen Wert erzeugen kann, der nicht zu einem Roundtrip führt.

Schemaüberlegungen

Eine Erläuterung dazu, welches Schema für Datentypen erstellt wird, finden Sie unter Data Contract Schema Reference.

Das Schema WCF erstellt für Datenvertragstypen keine Bestimmungen für die Versionsverwaltung. Das Schema, das aus einer bestimmten Version eines Typs exportiert wurde, enthält nur die Datenelemente, die in dieser Version vorhanden sind. Durch die Implementierung der Schnittstelle wird das IExtensibleDataObject Schema für einen Typ nicht geändert.

Datenmitglieder werden standardmäßig als optionale Elemente in das Schema exportiert. Das heißt, der minOccurs Wert (XML-Attribut) wird auf 0 festgelegt. Erforderliche Datenmember werden exportiert, wenn minOccurs auf 1 festgelegt wurde.

Viele der Änderungen, die als nicht-destruktiv betrachtet werden, sind tatsächlich disruptiv, wenn eine strikte Einhaltung des Schemas erforderlich ist. Im vorherigen Beispiel würde eine CarV1 Instanz mit nur dem Model Element anhand des CarV2 Schemas überprüft werden (das sowohl Model als auch Horsepower enthält, aber beide sind optional). Die Umgekehrte ist jedoch nicht wahr: Eine Instanz würde eine CarV2 Überprüfung gegen das CarV1 Schema fehlschlagen.

Round-Tripping bringt auch einige zusätzliche Überlegungen mit sich. Weitere Informationen finden Sie im Abschnitt "Schemaüberlegungen" in Forward-Compatible Datenverträge.

Andere zulässige Änderungen

Die Implementierung der IExtensibleDataObject-Schnittstelle ist ein "Non-Breaking Change". Es besteht jedoch kein Round-Tripping-Support für Versionen des Typs vor der Version, in der IExtensibleDataObject implementiert wurde. Weitere Informationen finden Sie unter Forward-Compatible Datenverträge.

Enumerationen

Das Hinzufügen oder Entfernen eines Enumerationselements ist eine bedeutende Änderung. Die Änderung des Namens einer Enumeration ist "breaking", sofern nicht der Vertragsname durch die Verwendung des Attributs EnumMemberAttribute der gleiche wie in der alten Version geblieben ist. Weitere Informationen finden Sie unter Enumerationstypen in Datenverträgen.

Sammlungen

Die meisten Sammlungsänderungen sind "non-breaking", da die meisten Sammlungstypen im Datenvertragsmodell gegeneinander austauschbar sind. Eine nicht angepasste Sammlung angepasst zu machen oder umgekehrt ist jedoch ein "Breaking Change". Außerdem ist das Ändern der Anpassungseinstellungen der Sammlung eine einschneidende Änderung; das bedeutet, dass der Datenvertragsname und der Namespace, der Name des wiederholten Elements, der Name des Schlüsselelements und der Name des Wertelements geändert werden. Weitere Informationen zur Sammlungsanpassung finden Sie unter Sammlungstypen in Datenverträgen.
Natürlich ist das Ändern des Datenvertrags der Inhalte einer Auflistung (z. B. das Ändern von einer Liste mit Ganzzahlen zu einer Liste mit Zeichenfolgen) eine einschneidende Änderung.

Siehe auch