Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
När programmen utvecklas kan du också behöva ändra de datakontrakt som tjänsterna använder. Det här avsnittet beskriver hur du skapar versionsdatakontrakt. I det här avsnittet beskrivs mekanismerna för versionshantering av datakontrakt. En fullständig översikt och vägledning om normativ versionshantering finns i Metodtips: Versionshantering för datakontrakt.
Bakåtkompatibla vs. Icke-bakåtkompatibla ändringar
Ändringar i ett datakontrakt kan vara bakåtkompatibla eller icke-bakåtkompatibla. När ett datakontrakt ändras på ett icke-banbrytande sätt kan ett program som använder den äldre versionen av kontraktet kommunicera med ett program med den nyare versionen, och ett program som använder den nyare versionen av kontraktet kan kommunicera med ett program med den äldre versionen. Å andra sidan innebär en förändring som bryter kompatibiliteten att kommunikationen förhindras i en eller båda riktningarna.
Alla ändringar av en typ som inte påverkar hur den överförs och tas emot är icke-inbrytbara. Sådana ändringar ändrar inte datakontraktet, bara den underliggande typen. Du kan till exempel ändra namnet på ett fält på ett icke-brytande sätt om du sedan anger Name-egenskapen och DataMemberAttribute-egenskapen till det äldre versionsnamnet. Följande kod visar version 1 av ett datakontrakt.
// Version 1
[DataContract]
public class Person
{
[DataMember]
private string Phone;
}
' Version 1
<DataContract()> _
Public Class Person
<DataMember()> _
Private Phone As String
End Class
Följande kod visar en icke-inbrytningsändring.
// 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
Vissa ändringar ändrar de överförda data, men det kan hända att de inte går sönder. Följande ändringar är alltid omstörtande:
Ändra ordningen på datamedlemmar med hjälp av Order-egenskapen för DataMemberAttribute.
Byta namn på en datamedlem.
Ändra datakontraktet för en datamedlemsvariabel. Du kan till exempel ändra typen av datamedlem från ett heltal till en sträng, eller från en typ med ett datakontrakt med namnet "Kund" till en typ med ett datakontrakt med namnet "Person".
Följande ändringar är också möjliga.
Lägga till och ta bort datamedlemmar
I de flesta fall är det inte en ändring som bryter bakåtkompatibilitet att lägga till eller ta bort en datamedlem, såvida du inte behöver strikt schemagiltighet (nya instanser validerar mot det gamla schemat).
När en typ med ett extra fält deserialiseras till en typ med ett fält som saknas ignoreras den extra informationen. (Den kan också lagras i tur- och retursyfte. Mer information finns iForward-Compatible datakontrakt).
När en typ med ett fält som saknas deserialiseras till en typ med ett extra fält, lämnas det extra fältet vid standardvärdet, vanligtvis noll eller null
. (Standardvärdet kan ändras. Mer information finns iVersion-Tolerant Serialiseringsåteranrop.)
Du kan till exempel använda CarV1
klassen på en klient och CarV2
klassen på en tjänst, eller så kan du använda CarV1
klassen på en tjänst och CarV2
klassen på en klient.
// 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
Version 2-slutpunkten kan skicka data till slutpunkten version 1. Serialisering av version 2 av datakontraktet Car
ger XML som liknar följande.
<Car>
<Model>Porsche</Model>
<HorsePower>300</HorsePower>
</Car>
Deserialiseringsmotorn på V1 hittar ingen matchande datamedlem för HorsePower
fältet och tar bort dessa data.
Dessutom kan version 1-slutpunkten skicka data till slutpunkten version 2. Serialisering av version 1 av datakontraktet Car
ger XML som liknar följande.
<Car>
<Model>Porsche</Model>
</Car>
Version 2 deserializer vet inte vad fältet HorsePower
ska anges till eftersom det inte finns några matchande data i inkommande XML. I stället är fältet inställt på standardvärdet 0.
Obligatoriska datamedlemmar
En datamedlemmen kan markeras som obligatorisk genom att ange egenskapen IsRequired för DataMemberAttribute till true
. Om nödvändiga data saknas vid deserialisering genereras ett undantag i stället för att datamedlemmen anges till standardvärdet.
Att lägga till en obligatorisk datamedlem är en skadlig ändring. Den nyare typen kan alltså fortfarande skickas till slutpunkter med den äldre typen, men inte tvärtom. Att ta bort en datamedlem som har markerats som obligatorisk i en tidigare version är också en icke-bakåtkompatibel ändring.
Att ändra egenskapsvärdet IsRequired från true
till false
är inte brytande, men att ändra det från false
till true
kan vara brytande om någon tidigare version av typen inte har den aktuella datamedlemmen.
Anmärkning
Även om egenskapen IsRequired är inställd på true
kan inkommande data vara null eller noll, och en typ måste vara beredd att hantera den här möjligheten. Använd inte IsRequired som en säkerhetsmekanism för att skydda mot felaktiga inkommande data.
Utelämnade standardvärden
Det är möjligt (men rekommenderas inte) att ange EmitDefaultValue
egenskapen för attributet DataMemberAttribute till false
, enligt beskrivningen i Standardvärden för datamedlem. Om den här inställningen är false
, kommer datamedlemmen inte att genereras om den är inställd på sitt standardvärde (vanligtvis null eller noll). Detta är inte kompatibelt med obligatoriska datamedlemmar i olika versioner på två sätt:
Ett datakontrakt med en datamedlem som krävs i en version kan inte ta emot standarddata (null eller noll) från en annan version där datamedlemmen har
EmitDefaultValue
angett tillfalse
.En obligatorisk datamedlem som har
EmitDefaultValue
angetts tillfalse
kan inte användas för att serialisera standardvärdet (null eller noll), men kan ta emot ett sådant värde vid deserialisering. Detta skapar ett rundreseproblem (data kan läsas in men kan sedan inte skrivas ut på samma sätt). Därför, omIsRequired
ärtrue
ochEmitDefaultValue
ärfalse
i en version bör samma kombination gälla för alla andra versioner så att ingen version av datakontraktet skulle kunna generera ett värde som inte går fram och tillbaka.
Schemaöverväganden
En förklaring av vilket schema som skapas för datakontraktstyper finns i Referens för datakontraktsschema.
Schemat som WCF skapar för datakontraktstyper gör inga avsättningar för versionshantering. Schemat som exporteras från en viss version av en typ innehåller alltså endast de datamedlemmar som finns i den versionen. Implementeringen av IExtensibleDataObject gränssnittet ändrar inte schemat för en typ.
Datamedlemmar exporteras till schemat som valfria element som standard.
minOccurs
Värdet (XML-attribut) är alltså inställt på 0. Nödvändiga datamedlemmar exporteras med minOccurs
värdet 1.
Många av de ändringar som anses vara icke-inbrytande bryter faktiskt om strikt efterlevnad av schemat krävs. I föregående exempel verifierar en CarV1
instans med bara elementet Model
mot CarV2
schemat (som har både Model
och Horsepower
, men båda är valfria). Det omvända är dock inte sant: en CarV2
instans skulle misslyckas med valideringen mot CarV1
schemat.
Round-tripping-processen medför också några extra hänsyn. Mer information finns i avsnittet "Schemaöverväganden" i Forward-Compatible datakontrakt.
Andra tillåtna ändringar
Implementering av IExtensibleDataObject gränssnittet är en icke-banbrytande ändring. Stöd för rundtripp finns dock inte för versioner före den version i vilken IExtensibleDataObject implementerades. Mer information finns iForward-Compatible datakontrakt.
Uppräkningar
Att lägga till eller ta bort en uppräkningsmedlem är en icke-bakåtkompatibel ändring. Att ändra namnet på en uppräkningsmedlem är brytande, såvida inte dess kontraktsnamn behålls på samma sätt som i den gamla versionen genom att använda attributet EnumMemberAttribute
. Mer information finns i Uppräkningstyper i datakontrakt.
Samlingar
De flesta samlingsändringar är icke-brytbara eftersom de flesta samlingstyper är utbytbara med varandra i datakontraktsmodellen. Att göra en icke-anpassad samling anpassad eller vice versa är dock en ändring som bryter kommandot. Dessutom är det en brytande ändring att ändra inställningarna för samlingens anpassning; det vill säga att ändra dess namn och namnrymd för datakontrakt, upprepande elementnamn, nyckelelementnamn och värdeelementnamn. Mer information om insamlingsanpassning finns i Samlingstyper i Datakontrakt.
Att ändra datakontraktet för innehållet i en samling (till exempel att ändra från en lista med heltal till en lista med strängar) är naturligtvis en icke-bakåtkompatibel ändring.