資料合約版本控制

隨著應用程式的發展,您也必須變更服務所使用的資料合約。 本主題說明如何設定資料合約的版本。 本主題描述資料合約版本控制的機制。 如需完整的概觀和版本控制指導方針的詳細規定,請參閱最佳做法:資料合約版本控制

中斷與不中斷變更的比較

資料合約的變更可以採用中斷或不中斷的方式進行。 當資料合約以不中斷的方式變更時,使用舊版合約的應用程式可以與使用新版合約的應用程式通訊,而使用新版合約的應用程式可以與使用舊版合約的應用程式通訊。 相反的,中斷變更則會阻止單向或雙向的通訊。

任何對類型的變更若不會影響其傳輸和接收的方式,則都是不中斷的變更。 此類的變更並不會改變資料合約,只會改變基礎型別。 例如,如果您接著將 NameDataMemberAttribute 屬性設定為舊版的名稱,則您就可以使用不中斷方式變更欄位的名稱。 下列程式碼說明資料合約的第 1 版。

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

下列程式碼示範不中斷變更。

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

某些變更會修改傳輸的資料,但可能會、也可能不會中斷。 下列的變更則一定會中斷:

  • 變更資料合約的 NameNamespace 值。

  • 使用 OrderDataMemberAttribute 屬性變更資料成員的順序。

  • 重新命名資料成員。

  • 變更資料成員的資料合約。 例如,將資料成員的型別從整數變更為字串,或從具有名為 "Customer" 資料合約的型別變更成具有名為 "Person" 資料合約的型別。

以下變更是允許的。

新增及移除資料成員

大部份的情況下,新增或移除資料成員不是採用中斷變更的方式,除非您要求嚴格的結構描述有效性 (根據舊結構描述驗證新執行個體)。

在將具有額外欄位的類型還原序列化為具有缺少欄位的類型時,則會忽略額外的資訊 (它也可以為了往返的目的而被儲存;如需詳細資訊,請參閱向前相容資料合約)。

在將缺少欄位的類型還原序列化為具有額外欄位的類型時,額外欄位會保留其預設值,通常為零或 null (預設值可能會變更;如需詳細資訊,請參閱版本相容序列化回撥。)

例如,您可以在用戶端上使用 CarV1 類別,並在服務上使用 CarV2 類別,或者可以在服務上使用 CarV1 類別,而在用戶端上使用 CarV2 類別。

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

第 2 版的端點可以成功地將資料傳送至第 1 版的端點。 序列化第 2 版的 Car 資料合約,會產生與下面類似的 XML。

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

第 1 版上的還原序列化引擎找不到與 HorsePower 欄位相符的資料成員,然後便捨棄該資料。

同時,第 1 版的端點可以將資料傳送至第 2 版的端點。 序列化第 1 版的 Car 資料合約,會產生與下列類似的 XML。

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

因為傳入的 XML 未包含符合的資料,因此第 2 版的還原序列化程式不知道要將 HorsePower 欄位設定為什麼。 相反的,該欄位會設為預設值 0。

必要的資料成員

IsRequiredDataMemberAttribute 屬性設定為 true,即可將資料成員標記為必要項。 如果在還原序列化時遺失了必要的資料,則會擲回例外狀況,而不是將該資料成員設定為預設值。

新增必要的資料成員是一種中斷變更。 也就是,較新的類型仍舊可以傳送至使用舊類型的端點,而不是將較舊的類型傳送至使用新類型的端點。 將任何舊版內已標記為必要項之資料成員移除,也是一種中斷變更。

IsRequired 屬性從 true 變更為 false 的動作是不中斷變更,但是將其從 false 變更為 true 的動作則可能會是中斷變更 (如果任何舊版的類型未含有所討論的資料成員)。

注意

雖然將 IsRequired 屬性設定為 true,但傳入的資料可能會為 null 或零,而您必須準備一個類型來處理這種可能性。 請勿使用 IsRequired 做為防止傳入資料損壞的安全性機制。

省略的預設值

您也可以根據資料成員預設值所述,將 DataMemberAttribute 屬性 (Attribute) 上的 EmitDefaultValue 屬性 (Property) 設為 false,但是不建議這麼做。 如果這個設定為 false,則如果資料成員設定為預設值 (通常為 null 或零),將不會予以省略。 在以下兩方面,這會與不同版本中之必要資料成員不相容:

  • 在某個版本內要求具有資料成員的資料合約,無法從不同版本 (其 EmitDefaultValue 設定為 false) 接收預設 (null 或零) 資料。

  • 具有 EmitDefaultValue 設定為 false 的必要資料成員,無法用來序列化其預設值 (null 或零),但是可以在還原序列化時接收此類的值。 如此會產生反覆存取的問題 (可以讀入資料,但卻無法接著將相同的資料寫出)。 因此,如果在某個版本內,IsRequiredtrue,且 EmitDefaultValuefalse,則相同的組合應套用至所有其他版本,此類沒有資料合約的版本將能夠產生不會導致反覆存取的值。

結構描述的考量

如需資料合約類型會產生哪些結構描述的說明,請參閱資料合約結構描述參考

為資料合約類型產生的結構描述 WCF 沒有對版本進行規定。 也就是說,從某些類型的版本所匯出的結構描述,只會包含該版本內存在的資料成員。 實作 IExtensibleDataObject 介面不會變更類型的結構描述。

匯出至結構描述的資料成員,預設會做為選擇性項目。 亦即,minOccurs (XML 屬性) 值設定為 0。 必要的資料成員會以設定為 1 的 minOccurs 匯出。

如果要求嚴格遵循結構描述,則許多被視為是不中斷的變更實際上是中斷變更。 在上述範例中,只具有 CarV1 項目的 Model 執行個體會根據 CarV2 結構描述 (即具有 ModelHorsepower 兩者,但兩者皆是選用項目) 進行驗證。 不過,反向並不成立:CarV2 執行個體根據 CarV1 結構描述所進行的驗證則可能會失敗。

往返還需要一些其他考量。 如需詳細資訊,請參閱向前相容資料合約中的「結構描述考量」一節。

其他允許的變更

實作 IExtensibleDataObject 介面為不中斷變更。 不過,實作 IExtensibleDataObject 的版本之前的類型版本,並不存在反覆存取支援。 如需詳細資訊,請參閱向前相容資料合約

列舉

新增或移除列舉型別成員是中斷變更。 變更列舉型別成員的名稱是中斷變更,除非其合約名稱保持為與舊版中使用 EnumMemberAttribute 屬性的名稱相同。 如需詳細資訊,請參閱資料合約中的列舉型別

集合

大多數的集合變更是不中斷的,因為大部份的集合類型可以在資料合約模型內互相交換。 不過,將非自訂的集合進行自訂 (反之亦然) 則是中斷變更。 同時,變更集合的自訂設定是中斷變更,也就是變更其資料合約名稱和命名空間、重複元素名稱、主要元素名稱,以及值元素名稱。 如需集合自訂項目的詳細資訊,請參閱資料合約中的集合型別
當然,變更集合之內容的資料合約 (例如,從整數的清單變更為字串的清單) 是中斷變更。

另請參閱