Condividi tramite


Versionamento del contratto di dati

Man mano che le applicazioni si evolvono, potrebbe anche essere necessario modificare i contratti dati usati dai servizi. In questo argomento viene illustrato come eseguire la versione dei contratti dati. In questo argomento vengono descritti i meccanismi di controllo delle versioni del contratto dati. Per una panoramica completa e linee guida per il controllo delle versioni prescrittive, vedere Procedure consigliate : Controllo delle versioni del contratto dati.

Modifiche che rompono vs. Modifiche senza interruzioni

Le modifiche apportate a un contratto dati possono causare interruzioni o non interruzioni. Quando un contratto dati viene modificato in modo compatibile, un'applicazione che utilizza la versione precedente del contratto può comunicare con un'applicazione che utilizza la versione più recente, e un'applicazione che utilizza la versione più recente del contratto può comunicare con un'applicazione che utilizza la versione precedente. D'altra parte, una modifica che causa un'interruzione impedisce la comunicazione in una o entrambe le direzioni.

Le modifiche apportate a un tipo che non influiscono sulla modalità di trasmissione e ricezione non causano interruzioni. Tali modifiche non modificano il contratto dati, ma solo il tipo sottostante. Ad esempio, è possibile modificare il nome di un campo in modo non distruttivo se si imposta la Name proprietà di DataMemberAttribute sul nome della versione precedente. Il codice seguente mostra la versione 1 di un contratto dati.

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

Il codice seguente mostra una modifica senza interruzioni.

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

Alcune modifiche alterano i dati trasmessi, ma potrebbero causare o meno problemi. Le modifiche seguenti causano sempre un'interruzione:

  • Modifica del valore Name o Namespace di un contratto di dati.

  • Modificare l'ordine dei membri dati usando la proprietà Order di DataMemberAttribute.

  • Ridenominazione di un membro di dati.

  • Modifica del contratto di dati per un membro dati. Ad esempio, modificando il tipo di membro dati da un numero intero a una stringa o da un tipo con un contratto dati denominato "Customer" a un tipo con un contratto dati denominato "Person".

Sono inoltre possibili le modifiche seguenti.

Aggiunta e rimozione di membri dati

Nella maggior parte dei casi, l'aggiunta o la rimozione di un membro dati non è una modifica che causa un'interruzione, a meno che non sia necessaria una rigorosa validità dello schema (nuove istanze che convalidano lo schema precedente).

Quando un tipo con un campo aggiuntivo viene deserializzato in un tipo con un campo mancante, le informazioni aggiuntive vengono ignorate. (Può anche essere archiviato per scopi di round trip; per altre informazioni, vedere Forward-Compatible Contratti dati).

Quando un tipo con un campo mancante viene deserializzato in un tipo con un campo aggiuntivo, il campo aggiuntivo viene lasciato al valore predefinito, in genere zero o null. È possibile modificare il valore predefinito; per ulteriori informazioni, vedere Version-Tolerant Callback di Serializzazione.

Ad esempio, è possibile usare la CarV1 classe in un client e la CarV2 classe in un servizio oppure è possibile usare la CarV1 classe in un servizio e la CarV2 classe in un client.

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

L'endpoint versione 2 può inviare correttamente i dati all'endpoint versione 1. La serializzazione della versione 2 del Car contratto dati restituisce codice XML simile al seguente.

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

Il motore di deserializzazione su V1 non trova un membro dati corrispondente per il HorsePower campo e scarta tali dati.

Inoltre, l'endpoint versione 1 può inviare dati all'endpoint della versione 2. La serializzazione della versione 1 del Car contratto dati restituisce codice XML simile al seguente.

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

Il deserializzatore versione 2 non sa cosa impostare il HorsePower campo su, perché non sono presenti dati corrispondenti nel codice XML in ingresso. Il campo viene invece impostato sul valore predefinito 0.

Membri dati obbligatori

Un membro dati può essere contrassegnato come richiesto impostando la IsRequired proprietà di DataMemberAttribute su true. Se i dati necessari mancano durante la deserializzazione, viene generata un'eccezione anziché impostare il membro dati sul valore predefinito.

L'aggiunta di un membro dati obbligatorio è un cambiamento significativo. Ovvero, il tipo più recente può ancora essere inviato agli endpoint con il tipo precedente, ma non viceversa. Anche la rimozione di un membro dati contrassegnato come richiesto in qualsiasi versione precedente è una modifica che causa un'interruzione.

La modifica del valore della IsRequired proprietà da true a false non causa interruzioni, ma la modifica di da false a true potrebbe causare un'interruzione se le versioni precedenti del tipo non hanno il membro dati in questione.

Annotazioni

Anche se la IsRequired proprietà è impostata su true, i dati in ingresso possono essere null o zero e un tipo deve essere preparato per gestire questa possibilità. Non usare IsRequired come meccanismo di sicurezza per proteggersi da dati in ingresso non valido.

Valori predefiniti omessi

È possibile (anche se non consigliato) impostare la EmitDefaultValue proprietà sull'attributo DataMemberAttribute su false, come descritto in Valori predefiniti del membro dati. Se questa impostazione è false, il membro dati non verrà generato se è impostato sul valore predefinito (in genere null o zero). Non è compatibile con i membri dati obbligatori in versioni diverse in due modi:

  • Un contratto dati con un membro dati richiesto in una versione non può ricevere dati predefiniti (null o zero) da una versione diversa in cui il membro dati è EmitDefaultValue impostato su false.

  • Un membro EmitDefaultValue dati obbligatorio impostato su false non può essere utilizzato per serializzare il valore predefinito (null o zero), ma può ricevere tale valore alla deserializzazione. In questo modo si crea un problema di scambio bidirezionale (i dati possono essere letti, ma gli stessi dati non possono poi essere scritti). Pertanto, se IsRequired è true e EmitDefaultValue è false in una versione, la stessa combinazione deve essere applicata a tutte le altre versioni in modo che nessuna versione del contratto dati sia in grado di produrre un valore che non comporta un round trip.

Considerazioni sullo schema

Per una spiegazione dello schema generato per i tipi di contratto dati, vedere Riferimento allo schema del contratto dati.

Lo schema che WCF produce per i tipi di contratto di dati non prevede alcun versionamento. Ovvero, lo schema esportato da una determinata versione di un tipo contiene solo i membri dati presenti in tale versione. L'implementazione dell'interfaccia IExtensibleDataObject non modifica lo schema per un tipo.

Per impostazione predefinita, i membri dati vengono esportati nello schema come elementi facoltativi. Ovvero, il minOccurs valore (attributo XML) è impostato su 0. I membri dati obbligatori vengono esportati con minOccurs impostato su 1.

Molte delle modifiche considerate non-interrompenti stanno effettivamente interrompendo se è necessaria una rigorosa conformità allo schema. Nell'esempio precedente, un'istanza CarV1 con solo l'elemento Model verrebbe convalidata rispetto allo CarV2 schema ,che ha entrambi Model e Horsepower, ma entrambi sono facoltativi. Tuttavia, il contrario non è vero: un'istanza CarV2 fallirebbe la convalida rispetto allo schema CarV1.

Il round trip comporta anche alcune considerazioni aggiuntive. Per altre informazioni, vedere la sezione "Considerazioni sullo schema" in Forward-Compatible Contratti dati.

Altre modifiche consentite

L'implementazione dell'interfaccia IExtensibleDataObject è una modifica senza impatti. Tuttavia, il supporto per il round trip non esiste per le versioni del tipo precedenti alla versione in cui IExtensibleDataObject è stata implementata. Per altre informazioni, vedere Forward-Compatible Contratti dati.

Enumerazioni

L'aggiunta o la rimozione di un membro di enumerazione è una modifica che causa un'interruzione. La modifica del nome di un membro di enumerazione causa un'interruzione, a meno che il nome del contratto non venga mantenuto uguale a quello della versione precedente usando l'attributo EnumMemberAttribute . Per altre informazioni, vedere Tipi di enumerazione nei contratti dati.

Collezioni

La maggior parte delle modifiche alla raccolta non causa interruzioni perché la maggior parte dei tipi di raccolta è intercambiabile tra loro nel modello di contratto dati. Tuttavia, trasformare una raccolta non personalizzata in personalizzata, o viceversa, è un cambiamento significativo. Inoltre, modificare le impostazioni di personalizzazione della raccolta rappresenta una modifica critica; ovvero, cambiare il nome e lo spazio dei nomi del contratto dati, il nome dell'elemento ripetuto, il nome dell'elemento chiave e il nome dell'elemento del valore. Per altre informazioni sulla personalizzazione della raccolta, vedere Tipi di raccolta in Contratti dati.
Naturalmente, la modifica del contratto di dati del contenuto di una raccolta (ad esempio, la modifica da un elenco di numeri interi a un elenco di stringhe) è una modifica significativa.

Vedere anche