애플리케이션이 발전함에 따라 서비스에서 사용하는 데이터 계약을 변경해야 할 수도 있습니다. 이 항목에서는 데이터 계약의 버전을 지정하는 방법을 설명합니다. 이 항목에서는 데이터 계약 버전 관리 메커니즘에 대해 설명합니다. 전체 개요 및 규범적인 버전 관리 지침은 모범 사례: 데이터 계약 버전 관리를 참조하세요.
호환성을 깨뜨리는 변경사항 vs. 호환성을 유지하는 변경사항
데이터 계약의 변경 사항은 호환성이 깨지거나 문제가 없을 수 있습니다. 데이터 계약이 변경되지 않는 방식으로 변경되면 이전 버전의 계약을 사용하는 애플리케이션이 최신 버전을 사용하여 애플리케이션과 통신할 수 있으며, 최신 버전의 계약을 사용하는 애플리케이션은 이전 버전을 사용하여 애플리케이션과 통신할 수 있습니다. 반면에 파괴적 변경으로 인해 한 방향 또는 양방향으로 통신할 수 없게 됩니다.
전송 및 수신 방법에 영향을 주지 않는 형식에 대한 변경 내용은 변경되지 않습니다. 이러한 변경은 데이터 계약을 변경하지 않고 기본 형식만 변경합니다. 예를 들어, Name의 DataMemberAttribute 속성을 이전 버전 이름으로 설정하면, 필드의 이름을 영향을 주지 않는 방식으로 변경할 수 있습니다. 다음 코드는 데이터 계약의 버전 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
일부 변경 내용은 전송된 데이터를 수정하지만 호환성이 손상되거나 손상되지 않을 수 있습니다. 다음과 같은 변경 사항은 항상 문제를 일으킵니다.
Order의 DataMemberAttribute 속성을 사용하여 데이터 멤버의 순서를 변경하기.
데이터 멤버 이름을 변경합니다.
데이터 멤버의 데이터 계약 변경 예를 들어 데이터 멤버의 형식을 정수에서 문자열로 변경하거나 데이터 계약이 "Customer"인 형식에서 "Person"이라는 데이터 계약이 있는 형식으로 변경합니다.
다음과 같은 변경도 가능합니다.
데이터 멤버 추가 및 제거
대부분의 경우 엄격한 스키마 유효성(이전 스키마에 대해 유효성을 검사하는 새 인스턴스)이 필요하지 않은 경우 데이터 멤버를 추가하거나 제거하는 것은 호환성이 손상되는 변경이 아닙니다.
추가 필드가 있는 형식이 누락된 필드가 있는 형식으로 역직렬화되면 추가 정보는 무시됩니다. (라운드트립 목적으로도 저장될 수 있습니다. 자세한 내용은 Forward-Compatible 데이터 계약 참조).
누락된 필드가 있는 형식이 추가 필드가 있는 형식으로 역직렬화되면 추가 필드는 기본값(일반적으로 0 또는 null
0)으로 유지됩니다. (기본값이 변경될 수 있습니다. 자세한 내용은 Version-Tolerant Serialization 콜백을 참조하세요.)
예를 들어, 클라이언트에 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>
V1의 역직렬화 엔진은 필드에 일치하는 데이터 멤버를 HorsePower
찾지 못하고 해당 데이터를 삭제합니다.
또한 버전 1 엔드포인트는 버전 2 엔드포인트로 데이터를 보낼 수 있습니다. 데이터 계약의 버전 1을 직렬화하면 Car
다음과 유사한 XML이 생성됩니다.
<Car>
<Model>Porsche</Model>
</Car>
들어오는 XML에 일치하는 데이터가 없기 때문에, 버전 2 역직렬 변환기는 HorsePower
필드를 무엇으로 설정해야 할지 알지 못합니다. 대신 필드는 기본값 0으로 설정됩니다.
필수 데이터 멤버
데이터 멤버는 IsRequired의 DataMemberAttribute 속성을 true
로 설정하여 필수 항목으로 표시될 수 있습니다. 역직렬화하는 동안 필요한 데이터가 누락된 경우 데이터 멤버를 기본값으로 설정하는 대신 예외가 throw됩니다.
필수 데이터 멤버가 추가된 것은 호환성이 손상되는 변경입니다. 즉, 최신 형식은 이전 형식의 엔드포인트로 계속 보낼 수 있지만, 이전 형식은 최신 형식의 엔드포인트로 보낼 수 없습니다. 이전 버전에서 필요에 따라 표시된 데이터 멤버를 제거하는 것도 호환성이 손상되는 변경입니다.
IsRequired 속성 값을 true
에서 false
로 변경하는 것은 중단되지 않지만, 값을 false
에서 true
로 변경하는 것이며 해당 데이터 멤버가 형식의 이전 버전에 없을 경우 변경 내용이 손상될 수 있습니다.
비고
속성이 IsRequired 설정 true
되었지만 들어오는 데이터는 null 또는 0일 수 있으며 이 가능성을 처리할 형식을 준비해야 합니다. 잘못된 들어오는 데이터로부터 보호하기 위해 보안 메커니즘으로 사용하지 IsRequired 마세요.
생략된 기본값
EmitDefaultValue
에서 설명된 대로 false
속성을 DataMemberAttribute 특징에 설정하는 것이 가능합니다(권장하지 않음). 이 설정이면 데이터 멤버가 false
기본값(일반적으로 null 또는 0)으로 설정된 경우 내보내지지 않습니다. 두 가지 방법으로 서로 다른 버전의 필수 데이터 멤버와 호환되지 않습니다.
한 버전에 필요한 데이터 멤버를 포함하는 데이터 계약은 데이터 멤버가
EmitDefaultValue
로 설정된false
다른 버전에서 기본값(null 또는 0) 데이터를 수신할 수 없습니다.EmitDefaultValue
로 설정된 필수 데이터 멤버는 기본값(null 또는 0)을 직렬화하는 데 사용될 수 없지만, 역직렬화 시 이러한 값을 받을 수 있습니다. 이렇게 하면 라운드트립 문제가 발생합니다(데이터를 읽을 수 있지만 동일한 데이터를 쓸 수 없음). 따라서IsRequired
이true
이고EmitDefaultValue
이false
인 경우, 데이터 계약의 어떤 버전도 왕복이 되지 않는 값을 생성할 수 없도록 모든 다른 버전에 동일한 조합이 적용되어야 합니다.
스키마 고려 사항
데이터 계약 형식에 대해 생성되는 스키마에 대한 설명은 데이터 계약 스키마 참조를 참조하세요.
데이터 계약 형식에 대해 생성되는 스키마 WCF는 버전 관리 프로비저닝을 제공하지 않습니다. 즉, 특정 버전의 형식에서 내보낸 스키마에는 해당 버전에 있는 데이터 멤버만 포함됩니다. 인터페이스를 IExtensibleDataObject 구현해도 형식에 대한 스키마는 변경되지 않습니다.
데이터 멤버는 기본적으로 선택적 요소로 스키마로 내보내집니다. 즉, minOccurs
(XML 특성) 값은 0으로 설정됩니다. 필수 데이터 멤버는 1로 minOccurs
설정하여 내보냅니다.
스키마를 엄격하게 준수해야 하는 경우 대부분의 변경 내용이 실제로 호환되지 않는 것으로 간주됩니다. 앞의 예에서, CarV1
요소만 있는 Model
인스턴스는 CarV2
스키마에 대해 유효성을 검사할 수 있습니다. 이 스키마는 Model
와 Horsepower
모두 포함하고 있지만, 둘 다 선택 사항입니다. 그러나 그 반대는 사실이 아닙니다. CarV2
인스턴스는 CarV1
스키마에 대한 유효성 검사를 통과하지 못할 것입니다.
또한 라운드트립에는 몇 가지 추가 고려 사항이 수반됩니다. 자세한 내용은 Forward-Compatible 데이터 계약의 "스키마 고려 사항" 섹션을 참조하세요.
기타 허용된 변경 내용
인터페이스 IExtensibleDataObject 구현은 비파괴적 변경입니다. 그러나 IExtensibleDataObject이(가) 구현된 버전 이전 형식 버전에서는 라운드트립 지원이 존재하지 않습니다. 자세한 내용은 Forward-Compatible 데이터 계약을 참조하세요.
열거 목록
열거형 멤버를 추가하거나 제거하는 것은 호환성을 깨뜨리는 변경입니다. 계약 이름이 EnumMemberAttribute
속성을 사용하여 이전 버전과 동일하게 유지되지 않는 한, 열거형 멤버의 이름 변경은 호환성에 문제가 발생합니다. 자세한 내용은 데이터 계약의 열거형 형식을 참조하세요.
수집품
대부분의 컬렉션 변경 내용은 대부분의 컬렉션 형식이 데이터 계약 모델에서 서로 교환할 수 있기 때문에 비호환적이지 않습니다. 사용자 지정이 아닌 컬렉션을 사용자 지정으로 만들거나 그 반대로 하는 것은 중대한 변경사항입니다. 또한 컬렉션의 사용자 지정 설정을 변경하는 것은 호환성이 손상되는 변경입니다. 즉, 데이터 계약 이름 및 네임스페이스 변경, 반복 요소 이름, 키 요소 이름 및 값 요소 이름입니다. 컬렉션 사용자 지정에 대한 자세한 내용은 데이터 계약의 컬렉션 형식을 참조하세요.
당연히 컬렉션 내용의 데이터 계약 변경(예: 정수 목록에서 문자열 목록으로 변경)은 호환성이 손상되는 변경입니다.