다음을 통해 공유


서비스 계약에서 데이터 전송 지정

WCF(Windows Communication Foundation)는 메시징 인프라로 생각할 수 있습니다. 서비스 작업에서는 메시지를 받고 처리한 다음 보낼 수 있습니다. 메시지는 작업 계약을 사용하여 설명됩니다. 다음 계약을 예로 들 수 있습니다.

[ServiceContract]
public interface IAirfareQuoteService
{
    [OperationContract]
    float GetAirfare(string fromCity, string toCity);
}

여기서 GetAirfare 작업은 fromCitytoCity에 대한 정보가 있는 메시지를 수락한 다음 숫자가 포함된 메시지를 반환합니다.

이 항목에서는 작업 계약에서 메시지를 설명할 수 있는 여러 가지 방법에 대해 설명합니다.

매개 변수를 사용하여 메시지 설명

메시지를 설명하는 가장 간단한 방법은 매개 변수 목록과 반환 값을 사용하는 것입니다. 앞의 예제에서 fromCitytoCity 문자열 매개 변수는 요청 메시지를 설명하는 데 사용되었고 float 반환 값은 회신 메시지를 설명하는 데 사용되었습니다. 반환 값으로만 회신 메시지를 설명할 수 없는 경우에는 out 매개 변수를 사용할 수도 있습니다. 예를 들어, 다음 연산의 경우 요청 메시지에는 fromCitytoCity가 있으며 회신 메시지에는 통화와 숫자가 함께 있습니다.

[OperationContract]
float GetAirfare(string fromCity, string toCity, out string currency);

또한 참조 매개 변수를 사용하여 요청 및 회신 메시지의 매개 변수 부분을 만들 수 있습니다. 매개 변수는 serialize할 수 있는, 즉 XML로 변환할 수 있는 형식이어야 합니다. 기본적으로 WCF에서는 DataContractSerializer 클래스라고 하는 구성 요소를 사용하여 이 변환을 수행합니다. int, string, floatDateTime과 같은 가장 기본적인 형식이 지원됩니다. 사용자 정의 형식에는 일반적으로 데이터 계약이 있어야 합니다. 자세한 내용은 데이터 계약 사용을 참조하십시오.

public interface IAirfareQuoteService
{
    [OperationContract]
    float GetAirfare(Itinerary itinerary, DateTime date);
    }
    [DataContract]
    public class Itinerary
    {
        [DataMember]
        public string fromCity;
        [DataMember]
        public string toCity;
}

DataContractSerializer가 사용자의 형식을 serialize하기에 적합하지 않은 경우도 있습니다. WCF에서는 대체 serialization 엔진인 XmlSerializer를 지원하므로, 이 엔진을 사용하여 매개 변수를 serialize할 수도 있습니다. XmlSerializer를 통해 XmlAttributeAttribute와 같은 특성을 사용하여 결과 XML을 보다 효과적으로 제어할 수 있습니다. 특정 연산이나 전체 서비스에 XmlSerializer를 사용하려면 XmlSerializerFormatAttribute 특성을 연산이나 서비스에 적용합니다. 예를 들면 다음과 같습니다.

[ServiceContract]
public interface IAirfareQuoteService
{
    [OperationContract]
    [XmlSerializerFormat]
    float GetAirfare(Itinerary itinerary, DateTime date);
}
public class Itinerary
{
    public string fromCity;
    public string toCity;
    [XmlAttribute]
    public bool isFirstClass;
}

자세한 내용은 XmlSerializer 클래스 사용를 참조하십시오. 위 항목에 설명된 대로 수동으로 XmlSerializer로 전환할 특별한 이유가 없는 한 이 예제에서처럼 수동으로 전환하지 않는 것이 좋습니다.

.NET 매개 변수 이름을 계약 이름으로부터 격리시키려면 MessageParameterAttribute 특성을 사용하고, Name 속성을 사용하여 계약 이름을 설정합니다. 예를 들면, 다음 작업 계약은 이 항목의 첫 번째 예제에 해당합니다.

[OperationContract]
public float GetAirfare(
    [MessageParameter(Name=”fromCity”)] string originCity,
    [MessageParameter(Name=”toCity”)] string destinationCity);

빈 메시지 설명

빈 요청 메시지는 입력 매개 변수나 참조 매개 변수를 사용하지 않고 설명할 수 있습니다. 예를 들면 다음과 같습니다.

[OperationContract]

public int GetCurrentTemperature();

빈 회신 메시지는 출력 또는 참조 매개 변수 없이 void 반환 형식을 사용하여 설명할 수 있습니다. 예를 들면 다음과 같습니다.

[OperationContract]
public void SetTemperature(int temperature);

이것은 다음과 같은 단방향 연산과는 다릅니다.

[OperationContract(IsOneWay=true)]
public void SetLightbulbStatus(bool isOn);

SetTemperatureStatus 연산은 빈 메시지를 반환합니다. 입력 메시지를 처리하는 데 문제가 있을 경우 대신 오류를 반환할 수도 있습니다. SetLightbulbStatus 연산은 아무 것도 반환하지 않습니다. 이 연산에서 오류 조건을 전달할 방법이 없습니다.

메시지 계약을 사용하여 메시지 설명

하나의 형식을 사용하여 전체 메시지를 나타내야 하는 경우가 있습니다. 이러한 목적을 위해 데이터 계약을 사용할 수는 있지만 메시지 계약을 사용하는 것이 좋습니다. 이렇게 하면 결과 XML에서 불필요한 래핑 수준이 생기지 않습니다. 또한 메시지 계약을 통해 결과 메시지를 보다 효과적으로 제어할 수 있습니다. 예를 들어, 메시지 본문에 필요한 정보와 메시지 헤더에 필요한 사항을 결정할 수 있습니다. 다음 예제에서는 메시지 계약을 사용하는 방법을 보여 줍니다.

[ServiceContract]
public interface IAirfareQuoteService
{
    [OperationContract]
    GetAirfareResponse GetAirfare(GetAirfareRequest request);
}

[MessageContract]
public class GetAirfareRequest
{
    [MessageHeader] public DateTime date;
    [MessageBodyMember] public Itinerary itinerary;
}

[MessageContract]
public class GetAirfareResponse
{
    [MessageBodyMember] public float airfare;
    [MessageBodyMember] public string currency;
}

[DataContract]
public class Itinerary
{
    [DataMember] public string fromCity;
    [DataMember] public string toCity;
}

자세한 내용은 메시지 계약 사용을 참조하십시오.

앞의 예제의 경우 DataContractSerializer 클래스가 기본적으로 사용됩니다. XmlSerializer 클래스는 메시지 계약에 사용할 수도 있습니다. 이 작업을 수행하려면 XmlSerializerFormatAttribute 특성을 연산이나 계약에 적용하고 메시지 헤더와 본문 멤버의 XmlSerializer 클래스와 호환되는 형식을 사용합니다.

스트림을 사용하여 메시지 설명

연산에서 메시지를 설명하는 또 다른 방법은 Stream 클래스 또는 작업 계약의 파생 클래스 중 하나를 사용하거나 메시지 계약 본문 멤버(이 경우 유일한 멤버여야 함)로 사용하는 것입니다. 들어오는 메시지의 경우 형식은 Stream이어야 하며, 파생 클래스는 사용할 수 없습니다.

WCF는 serializer를 호출하지 않는 대신, 스트림에서 데이터를 검색하여 보내는 메시지에 직접 넣거나 들어오는 메시지에서 데이터를 검색하여 스트림에 직접 넣습니다. 다음 예제에서는 스트림의 사용 방법을 보여 줍니다.

[OperationContract]
public Stream DownloadFile(string fileName);

하나의 메시지 본문에서 Stream과 비스트림 데이터를 결합할 수 없습니다. 메시지 헤더에 추가 데이터를 넣으려면 메시지 계약을 사용합니다. 다음 예제에서는 작업 계약을 정의할 때 잘못된 스트림 사용 방법을 보여 줍니다.

//Incorrect:
// [OperationContract]
// public void UploadFile (string fileName, Stream fileData);

다음 샘플에서는 작업 계약을 정의할 때 올바른 스트림 사용 방법을 보여 줍니다.

[OperationContract]
public void UploadFile (UploadFileMessage message);
//code omitted…
[MessageContract]
public class UploadFileMessage
{
    [MessageHeader] public string fileName;
    [MessageBodyMember] public Stream fileData;
}

자세한 내용은 큰 데이터 및 스트리밍을 참조하십시오.

Message 클래스 사용

보내거나 받은 메시지를 프로그래밍 방식으로 완벽하게 제어하려면 다음 예제 코드에서처럼 Message 클래스를 직접 사용합니다.

[OperationContract]
public void LogMessage(Message m);

이러한 고급 시나리오는 Message 클래스 사용에서 자세히 설명합니다.

오류 메시지 설명

반환 값과 출력 또는 참조 매개 변수를 사용하여 설명하는 메시지 이외에, 단방향이 아닌 연산은 적어도 두 개의 메시지, 즉 일반 응답 메시지와 오류 메시지를 반환할 수 있습니다. 다음 작업 계약을 예로 들 수 있습니다.

[OperationContract]
float GetAirfare(string fromCity, string toCity, DateTime date);

이 연산은 float 숫자가 포함된 일반 메시지 또는 오류 코드 및 설명이 포함된 오류 메시지를 반환할 수 있습니다. 서비스 구현에서 FaultException을 throw하여 이 연산을 수행할 수 있습니다.

FaultContractAttribute 특성을 사용하여 가능한 추가 오류 메시지를 지정할 수 있습니다. 추가 오류는 다음 예제 코드에서처럼 DataContractSerializer를 사용하여 serialize할 수 있어야 합니다.

[OperationContract]
[FaultContract(typeof(ItineraryNotAvailableFault))]
float GetAirfare(string fromCity, string toCity, DateTime date);

//code omitted…

[DataContract]
public class ItineraryNotAvailableFault
{
    [DataMember]
    public bool IsAlternativeDateAvailable;

    [DataMember]
    public DateTime alternativeSuggestedDate;
}

이러한 추가 오류는 해당하는 데이터 계약 형식의 FaultException을 throw하여 생성될 수 있습니다. 자세한 내용은 예외 및 오류 처리를 참조하십시오.

XmlSerializer 클래스를 사용하여 오류를 설명할 수 없습니다. XmlSerializerFormatAttribute는 오류 계약에 영향을 주지 않습니다.

파생 형식 사용

특정 연산 또는 메시지 계약에 기본 형식을 사용한 다음 실제로 연산을 호출할 때는 파생 형식을 사용해야 하는 경우가 있습니다. 이 경우 파생 형식을 사용할 수 있도록 ServiceKnownTypeAttribute 특성 또는 일부 대체 메커니즘을 사용해야 합니다. 다음 연산을 예로 들 수 있습니다.

[OperationContract]
public bool IsLibraryItemAvailable(LibraryItem item);

BookMagazine 이 두 형식이 LibraryItem에서 파생된다고 가정합니다. 이 형식을 IsLibraryItemAvailable 연산에서 사용하려면 다음과 같이 연산을 변경합니다.

[OperationContract]

[ServiceKnownType(typeof(Book))]

[ServiceKnownType(typeof(Magazine))]

public bool IsLibraryItemAvailable(LibraryItem item);

또는 다음 예제 코드에서처럼 기본 DataContractSerializer가 사용 중인 경우 KnownTypeAttribute 특성을 사용할 수 있습니다.

[OperationContract]
public bool IsLibraryItemAvailable(LibraryItem item);

// code omitted…

[DataContract]
[KnownType(typeof(Book))]
[KnownType(typeof(Magazine))]
public class LibraryItem
{
    //code omitted…
}

XmlSerializer를 사용할 때 XmlIncludeAttribute 특성을 사용할 수 있습니다.

특정 연산 또는 전체 서비스에 ServiceKnownTypeAttribute 특성을 적용할 수 있습니다. 호출할 메서드의 형식이나 이름을 사용하여 KnownTypeAttribute 특성과 같은 알려진 형식 목록을 가져옵니다. 자세한 내용은 데이터 계약 알려진 형식을 참조하십시오.

사용 및 스타일 지정

WSDL(웹 서비스 기술 언어)을 사용하여 서비스를 설명하는 경우 문서 스타일과 RPC(원격 프로시저 호출) 스타일이 일반적으로 사용됩니다. 문서 스타일에서 전체 메시지 본문은 스키마를 사용하여 설명되고 WSDL은 해당 스키마 내의 요소를 참조하여 여러 메시지 본문 부분을 설명합니다. RPC 스타일에서 WSDL은 요소가 아닌 각 메시지 부분의 스키마 형식을 참조합니다. 이 두 스타일 중 하나를 수동으로 선택해야 하는 경우도 있습니다. 둘 중 하나만 수동으로 설정하려면 DataContractSerializer가 사용 중인 경우에는 DataContractFormatAttribute 특성을 적용한 다음 Style 속성을 설정하고, XmlSerializer를 사용하는 경우에는 XmlSerializerFormatAttribute 특성에 Style을 설정합니다.

또한 XmlSerializer는 serialize된 두 가지 형식의 XML LiteralEncoded를 지원합니다. Literal은 가장 일반적인 형태로, DataContractSerializer가 지원하는 유일한 형식입니다. Encoded는 SOAP 사양의 5단원에 설명된 레거시 형식이며 새 서비스에는 사용하지 않는 것이 좋습니다. Encoded 모드로 전환하려면 XmlSerializerFormatAttribute 특성에서 Use 속성을 Encoded로 설정합니다.

대부분의 경우 StyleUse 속성에 대한 기본 설정을 변경하면 안 됩니다.

Serialization 프로세스 제어

여러 가지 방법으로 데이터를 serialize하는 방법을 사용자 지정할 수 있습니다.

서버 Serialization 설정 변경

기본 DataContractSerializer가 사용 중인 경우 ServiceBehaviorAttribute 특성을 서비스에 적용하여 서비스에서 serialization 프로세스의 몇 가지 특성을 제어할 수 있습니다. 특히 MaxItemsInObjectGraph 속성을 사용하여 DataContractSerializer가 deserialize하는 개체의 최대 개수를 제한하는 할당량을 설정할 수도 있습니다. IgnoreExtensionDataObject 속성을 사용하여 라운드트립 버전 관리 기능을 해제할 수 있습니다. 할당량에 대한 자세한 내용은 데이터에 대한 보안 고려 사항을 참조하십시오. 라운드트립에 대한 자세한 내용은 이후 버전과 호환되는 데이터 계약을 참조하십시오.

[ServiceContract]
[ServiceBehavior(MaxItemsInObjectGraph=100000)]
public interface IDataService
{
    [OperationContract] DataPoint[] GetData();
}

Serialization 동작

serializer가 특정 작업에 사용되고 있는지 여부에 따라 자동으로 연결되는 두 가지 동작, DataContractSerializerOperationBehaviorXmlSerializerOperationBehavior를 WCF에서 사용할 수 있습니다. 이러한 동작은 자동으로 적용되므로 일반적으로 사용자가 알아야 할 필요는 없습니다.

그러나 DataContractSerializerOperationBehavior에는 serialization 프로세스를 사용자 지정하는 데 사용할 수 있는 MaxItemsInObjectGraph, IgnoreExtensionDataObjectDataContractSurrogate 속성이 있습니다. 처음 두 속성은 앞 단원에서 설명한 것과 의미가 같습니다. DataContractSurrogate 속성을 사용하여 serialization 프로세스를 사용자 지정하고 확장하는 데 필요한 강력한 메커니즘인 데이터 계약 서로게이트를 사용하도록 설정할 수 있습니다. 자세한 내용은 데이터 계약 서로게이트를 참조하십시오.

DataContractSerializerOperationBehavior를 사용하여 클라이언트와 서버 serialization 모두를 사용자 지정할 수 있습니다. 다음 예제에서는 클라이언트에 MaxItemsInObjectGraph 할당량을 늘리는 방법을 보여 줍니다.

ChannelFactory<IDataService> factory = new ChannelFactory<IDataService>(binding, address);
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)
{
    DataContractSerializerOperationBehavior dataContractBehavior =
                op.Behaviors.Find<DataContractSerializerOperationBehavior>()
                as DataContractSerializerOperationBehavior;
    if (dataContractBehavior != null)
    {
        dataContractBehavior.MaxItemsInObjectGraph = 100000;
    }
}
IDataService client = factory.CreateChannel();

다음은 자체 호스팅된 경우 서비스의 해당 코드입니다.

ServiceHost serviceHost = new ServiceHost(typeof(IDataService))
foreach (ServiceEndpoint ep in serviceHost.Description.Endpoints)
{
foreach (OperationDescription op in ep.Contract.Operations)
{
        DataContractSerializerOperationBehavior dataContractBehavior =
           op.Behaviors.Find<DataContractSerializerOperationBehavior>()
                as DataContractSerializerOperationBehavior;
        if (dataContractBehavior != null)
        {
            dataContractBehavior.MaxItemsInObjectGraph = 100000;
        }
}
}
serviceHost.Open();

웹 호스팅의 경우 새 ServiceHost 파생 클래스를 만들고 서비스 호스트 팩터리를 사용하여 연결해야 합니다.

구성에서 Serialization 설정 제어

MaxItemsInObjectGraphIgnoreExtensionDataObject는 다음 예제에서처럼 dataContractSerializer 끝점 또는 서비스 동작을 사용하여 구성을 통해 제어될 수 있습니다.

<configuration>
    <system.serviceModel>
        <behaviors>
            <endpointBehaviors>
                <behavior name="LargeQuotaBehavior">
                    <dataContractSerializer
                      maxItemsInObjectGraph="100000" />
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <client>
            <endpoint address=http://example.com/myservice
                  behaviorConfiguration="LargeQuotaBehavior"
                binding="basicHttpBinding" bindingConfiguration="" 
                            contract="IDataService"
                name="" />
        </client>
    </system.serviceModel>
</configuration>

공유 형식 Serialization, 개체 그래프 유지 및 사용자 지정 Serializer

DataContractSerializer는 .NET 형식 이름이 아니라 데이터 계약 이름을 사용하여 serialize합니다. 이러한 serialization 방식은 서비스 기반 아키텍처 개념과 일치하며, 연결 계약에 영향을 주지 않고 .NET 형식을 변경할 수 있는 등 유연성을 높여 줍니다. 드문 경우지만 실제 .NET 형식 이름을 serialize해야 하는 경우가 있으며, 이 경우 .NET Framework Remoting 기술과 유사한 클라이언트와 서버 간 밀접한 결합이 가능합니다. 일반적으로 .NET Framework Remoting에서 WCF로 마이그레이션할 때 사용하는 드문 경우를 제외하고 이 방식은 권장되지 않습니다. 이 경우에는 DataContractSerializer 클래스 대신 NetDataContractSerializer 클래스를 사용해야 합니다.

DataContractSerializer는 일반적으로 개체 그래프를 개체 트리로 serialize합니다. 즉, 같은 개체를 두 번 이상 참조하는 경우 두 번 이상 serialize됩니다. billToshipTo라고 하는 두 가지 주소 형식 필드가 있는 PurchaseOrder 인스턴스를 예로 들 수 있습니다. 두 필드가 같은 주소 인스턴스로 설정된 경우 serialization 및 deserialization 후 두 개의 동일한 주소 인스턴스가 생깁니다. StyleUse에 대해 앞 단원에서 설명한 대로 XmlSerializer에서 사용할 수 있는 레거시 SOAP 인코딩 표준을 제외하고, XML로 개체 그래프를 나타낼 표준 상호 운용 가능한 방법이 없기 때문에 이와 같이 수행됩니다. 개체 그래프를 트리로 serialize하면 순환 참조가 있는 그래프를 serialize할 수 없는 등의 단점이 있습니다. 상호 운용이 가능하지 않아도 실제 개체 그래프 serialization으로 전환해야 하는 경우도 있습니다. 이 작업은 true로 설정된 preserveObjectReferences 매개 변수를 사용하여 생성된 DataContractSerializer를 사용하여 수행할 수 있습니다.

기본 제공 serializer가 사용자 시나리오에 적합하지 않은 경우도 있습니다. 대부분의 경우 DataContractSerializerNetDataContractSerializer가 모두 파생된 XmlObjectSerializer 추상화를 사용할 수 있습니다.

앞의 세 가지 경우(.NET 형식 유지, 개체 그래프 유지 및 XmlObjectSerializer 기반의 완전한 사용자 지정 serialization) 모두 사용자 지정 serializer를 연결해야 합니다. 이렇게 하려면 다음 단계를 수행합니다.

  1. DataContractSerializerOperationBehavior에서 파생되는 고유한 동작을 작성합니다.
  2. 두 개의 CreateSerializer 메서드를 재정의하여 고유한 serializer(NetDataContractSerializer, preserveObjectReferencestrue로 설정된 DataContractSerializer 또는 사용자 지정 XmlObjectSerializer)를 반환합니다.
  3. 서비스 호스트를 열거나 클라이언트 채널을 만들기 전에 기존 DataContractSerializerOperationBehavior 동작을 제거하고 이전 단계에서 만든 사용자 지정 파생 클래스에 연결합니다.

고급 serialization 개념에 대한 자세한 내용은 Serialization 및 Deserialization을 참조하십시오.

참고 항목

작업

방법: 스트리밍 사용
방법: 클래스 또는 구조체에 대한 기본 데이터 계약 만들기

개념

XmlSerializer 클래스 사용