共用方式為


在服務合約中指定數據傳輸

Windows Communication Foundation (WCF) 可以視為傳訊基礎結構。 服務作業可以接收訊息、處理訊息,以及傳送訊息。 訊息會使用作業合約來描述。 例如,請考慮下列合約。

[ServiceContract]  
public interface IAirfareQuoteService  
{  
    [OperationContract]  
    float GetAirfare(string fromCity, string toCity);  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
  
    <OperationContract()>  
    Function GetAirfare(fromCity As String, toCity As String) As Double  
End Interface  

在這裡,GetAirfare作業會接受包含 和fromCity相關信息的toCity訊息,然後傳回包含數位的訊息。

本主題說明作業合約可描述訊息的各種方式。

使用參數描述訊息

描述訊息最簡單的方式是使用參數清單和傳回值。 在上述範例中, fromCitytoCity 字串參數是用來描述要求訊息,而 float 傳回值則用來描述回復訊息。 如果單獨傳回值不足以描述回復訊息,可以使用 out 參數。 例如,以下操作的要求訊息中包含 fromCitytoCity,以及回覆訊息中的數字和貨幣。

[OperationContract]  
float GetAirfare(string fromCity, string toCity, out string currency);  
<OperationContract()>  
    Function GetAirfare(fromCity As String, toCity As String) As Double  

此外,您可以使用參考參數,讓參數成為要求和回復訊息的一部分。 參數必須是可以串行化的型別(轉換成 XML)。 根據預設,WCF 會使用稱為 類別的 DataContractSerializer 元件來執行此轉換。 支援大部分的基本類型(例如intstringfloatDateTime。)。 用戶定義型別通常必須有數據合約。 如需詳細資訊,請參閱 使用數據合約

public interface IAirfareQuoteService  
{  
    [OperationContract]  
    float GetAirfare(Itinerary itinerary, DateTime date);  
  
    [DataContract]  
    public class Itinerary  
    {  
        [DataMember]  
        public string fromCity;  
        [DataMember]  
        public string toCity;  
   }  
}  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    GetAirfare(itinerary as Itinerary, date as DateTime) as Double  
  
    <DataContract()>  
    Class Itinerary  
  
        <DataMember()>  
        Public fromCity As String  
        <DataMember()>  
        Public toCity As String  
    End Class  
End Interface  

有時候, DataContractSerializer 無法序列化您的類型。 WCF 支援替代串行化引擎 , XmlSerializer您也可以用來串行化參數。 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;  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    <XmlSerializerFormat>  
    GetAirfare(itinerary as Itinerary, date as DateTime) as Double  
  
End Interface  
  
Class Itinerary  
  
    Public fromCity As String  
    Public toCity As String  
    <XmlSerializerFormat()>  
    Public isFirstClass As Boolean  
End Class  

如需詳細資訊,請參閱 使用 XmlSerializer 類別。 請記住,除非您有如該主題中所述的特定原因,否則不建議手動切換至 XmlSerializer 此處所示。

若要隔離 .NET 參數名稱與合約名稱,您可以使用 MessageParameterAttribute 屬性,並使用 Name 屬性來設定合約名稱。 例如,下列作業合約相當於本主題中的第一個範例。

[OperationContract]  
public float GetAirfare(  
    [MessageParameter(Name="fromCity")] string originCity,  
    [MessageParameter(Name="toCity")] string destinationCity);  
<OperationContract()>  
  Function GetAirfare(<MessageParameter(Name := "fromCity")> fromCity As String, <MessageParameter(Name := "toCity")> toCity As String) As Double  

描述空白訊息

空的要求訊息可以透過沒有輸入或參考參數來描述。 例如,在 C# 中:

[OperationContract]

public int GetCurrentTemperature();

例如,在 Visual Basic 中:

<OperationContract()>

Function GetCurrentTemperature() as Integer

具有 void 傳回型別且沒有輸出或參考參數的回覆訊息可以被描述為空訊息。 例如:

[OperationContract]  
public void SetTemperature(int temperature);  
<OperationContract()>  
Sub SetTemperature(temperature As Integer)  

這與單向作業不同,例如:

[OperationContract(IsOneWay=true)]  
public void SetLightbulbStatus(bool isOn);  
<OperationContract(IsOneWay:=True)>  
Sub SetLightbulbStatus(isOne As Boolean)  

操作會 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;  
}  
<ServiceContract()>  
Public Interface IAirfareQuoteService  
    <OperationContract()>  
    Function GetAirfare(request As GetAirfareRequest) As GetAirfareResponse  
End Interface  
  
<MessageContract()>  
Public Class GetAirfareRequest  
    <MessageHeader()>
    Public Property date as DateTime  
    <MessageBodyMember()>  
    Public Property itinerary As Itinerary  
End Class  
  
<MessageContract()>  
Public Class GetAirfareResponse  
    <MessageBodyMember()>  
    Public Property airfare As Double  
    <MessageBodyMember()> Public Property currency As String  
End Class  
  
<DataContract()>  
Public Class Itinerary  
    <DataMember()> Public Property fromCity As String  
    <DataMember()> Public Property toCity As String  
End Class  

如需詳細資訊,請參閱 使用訊息合約

在上一個範例中, DataContractSerializer 預設仍會使用 類別。 類別 XmlSerializer 也可以與訊息合約搭配使用。 若要這樣做,請將 XmlSerializerFormatAttribute 屬性套用至作業或合約,並使用與 XmlSerializer 訊息標頭和本文成員中的 類別相容的類型。

使用數據流描述訊息

另一個在作業中描述訊息的方法,是使用 Stream 作業合約中的類別或其其中一個衍生類別,或做為訊息合約主體成員(在此情況下必須是唯一的成員)。 對於傳入訊息,類型必須是 Stream—您無法使用衍生類別。

WCF 不叫用串行化程式,而是從數據流擷取數據,並將其直接放入傳出訊息中,或從傳入訊息擷取數據,並將它直接放入數據流中。 下列範例顯示串流的使用。

[OperationContract]  
public Stream DownloadFile(string fileName);  
<OperationContract()>  
Function DownloadFile(fileName As String) As String  

您無法在單一訊息本文中合併 Stream 和非數據流數據。 使用訊息合約將額外的數據放在訊息標頭中。 下列範例顯示定義作業合約時,數據流的使用方式不正確。

//Incorrect:  
// [OperationContract]  
// public void UploadFile (string fileName, Stream fileData);  
'Incorrect:  
    '<OperationContract()>  
    Public Sub UploadFile(fileName As String, fileData As StreamingContext)  

下列範例顯示定義作業合約時,數據流的正確使用方式。

[OperationContract]  
public void UploadFile (UploadFileMessage message);  
//code omitted  
[MessageContract]  
public class UploadFileMessage  
{  
    [MessageHeader] public string fileName;  
    [MessageBodyMember] public Stream fileData;  
}  
<OperationContract()>  
Public Sub UploadFile(fileName As String, fileData As StreamingContext)  
'Code Omitted  
<MessageContract()>  
Public Class UploadFileMessage  
   <MessageHeader()>  
    Public Property fileName As String  
    <MessageBodyMember()>  
    Public Property fileData As Stream  
End Class  

如需詳細資訊,請參閱 大型數據和串流

使用 Message 類別

若要完全以程式設計方式控制傳送或接收的訊息,您可以直接使用 Message 類別,如下列範例程式代碼所示。

[OperationContract]  
public void LogMessage(Message m);  
<OperationContract()>  
Sub LogMessage(m As Message)  

這是進階案例,在 使用訊息類別中詳細說明。

描述錯誤訊息

除了傳回值和輸出或參考參數所描述的訊息之外,任何非單向作業都可以傳回至少兩個可能的訊息:其一般回應訊息和錯誤訊息。 請考慮下列作業合約。

[OperationContract]  
float GetAirfare(string fromCity, string toCity, DateTime date);  
<OperationContract()>  
Function GetAirfare(fromCity As String, toCity As String, date as DateTime)  

這項作業可能會傳回包含 float 數位的一般訊息,或包含錯誤碼和描述的錯誤訊息。 您可以在服務實作中擲回 FaultException 來達成此目的。

您可以使用 屬性來指定其他可能的錯誤訊息 FaultContractAttribute 。 其他錯誤必須使用 DataContractSerializer 進行串行化,如下列範例程式代碼所示。

[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;  
}  
<OperationContract()>  
<FaultContract(GetType(ItineraryNotAvailableFault))>  
Function GetAirfare(fromCity As String, toCity As String, date as DateTime) As Double  
  
'Code Omitted  
<DataContract()>  
Public Class  
  <DataMember()>  
  Public Property IsAlternativeDateAvailable As Boolean  
  <DataMember()>  
  Public Property alternativeSuggestedDate As DateTime  
End Class  

拋出符合的數據合約類型的 FaultException<TDetail> 可產生這些額外的錯誤。 如需詳細資訊,請參閱 處理例外狀況和錯誤

您無法使用 類別 XmlSerializer 來描述錯誤。 對 XmlSerializerFormatAttribute 錯誤合約沒有任何影響。

使用衍生類型

您可能想要在作業或訊息合約中使用基底類型,然後在實際叫用作業時使用衍生類型。 在此情況下,您必須使用 ServiceKnownTypeAttribute 屬性或一些替代機制,以允許使用衍生類型。 請考慮下列作業。

[OperationContract]  
public bool IsLibraryItemAvailable(LibraryItem item);  
<OperationContract()>  
    Function IsLibraryItemAvailable(item As LibraryItem) As Boolean  

假設兩種類型 BookMagazine 衍生自 LibraryItem。 在 IsLibraryItemAvailable 作業中使用這些類型,您可以按如下方式變更該作業:

[OperationContract]

[ServiceKnownType(typeof(Book))]

[ServiceKnownType(typeof(Magazine))]

public bool IsLibraryItemAvailable(LibraryItem item);

或者,當預設值KnownTypeAttribute正在使用時,您可以使用 DataContractSerializer 屬性,如下列範例程式代碼所示。

[OperationContract]  
public bool IsLibraryItemAvailable(LibraryItem item);  
  
// code omitted
  
[DataContract]  
[KnownType(typeof(Book))]  
[KnownType(typeof(Magazine))]  
public class LibraryItem  
{  
    //code omitted  
}  
<OperationContract()>  
Function IsLibraryItemAvailable(item As LibraryItem) As Boolean  
  
'Code Omitted  
<DataContract()>  
<KnownType(GetType(Book))>  
<KnownType(GetType(Magazine))>  
Public Class LibraryItem  
  'Code Omitted  
End Class  

當使用 XmlIncludeAttribute 時,您可以使用 XmlSerializer 屬性。

您可以將 屬性套用 ServiceKnownTypeAttribute 至作業或整個服務。 它能接受一個型別或一個需要呼叫的方法名稱,以取得已知型別的清單,就像 KnownTypeAttribute 屬性一樣。 如需詳細資訊,請參閱 數據合約已知類型

指定用途和風格

使用 Web 服務描述語言 (WSDL) 描述服務時,兩個常用的樣式是 Document 和 remote procedure call (RPC)。 在 [文件] 樣式中,會使用架構描述整個訊息本文,而 WSDL 會參考該架構內的元素來描述各種訊息本文部分。 在 RPC 樣式中,WSDL 是指每個訊息部分的架構類型,而不是元素。 在某些情況下,您必須手動選取其中一種樣式。 您可以套用 DataContractFormatAttribute 屬性並設定 Style 屬性(使用時DataContractSerializer),或在 屬性上Style設定 XmlSerializerFormatAttribute (使用 XmlSerializer時)。

此外,支援 XmlSerializer 兩種形式的序列化 XML:LiteralEncodedLiteral 是最常接受的表單,而且是唯一支援的表單 DataContractSerializerEncoded 是SOAP規格第5節中所述的舊版窗體,不建議用於新服務。 若要切換至 Encoded 模式,請將 Use 屬性上的 XmlSerializerFormatAttribute 屬性設定為 Encoded

在大部分情況下,您不應該變更 StyleUse 屬性的預設設定。

控制串行化進程

您可以採取多種方式來自定義資料序列化的方式。

變更伺服器串行化設定

使用預設值 DataContractSerializer 時,您可以在服務中套用 ServiceBehaviorAttribute 屬性,來控制序列化過程的一些層面。 具體而言,您可以使用 MaxItemsInObjectGraph 屬性來設定限制還原串行化物件 DataContractSerializer 數目上限的配額。 您可以使用 IgnoreExtensionDataObject 屬性來關閉來回版本控制功能。 如需配額的詳細資訊,請參閱 數據的安全性考慮。 如需有關來回處理的詳細資訊,請參閱 Forward-Compatible 數據合約

[ServiceBehavior(MaxItemsInObjectGraph=100000)]  
public class MyDataService:IDataService  
{  
    public DataPoint[] GetData()  
    {  
       // Implementation omitted  
    }  
}  
<ServiceBehavior(MaxItemsInObjectGraph:=100000)>  
Public Class MyDataService Implements IDataService  
  
    Function GetData() As DataPoint()  
         ‘ Implementation omitted  
    End Function  
End Interface  

串行化行為

WCF 中提供兩種行為,DataContractSerializerOperationBehavior 以及 XmlSerializerOperationBehavior,會根據特定作業所使用的串行化器自動嵌入。 由於這些行為會自動套用,因此您通常不需要注意這些行為。

不過,DataContractSerializerOperationBehavior 具有可用來自定義串行化過程的 MaxItemsInObjectGraphIgnoreExtensionDataObjectDataContractSurrogate 屬性。 前兩個屬性的意義與上一節所述相同。 您可以使用 DataContractSurrogate 屬性來啟用數據合約代理,這是自定義和擴充串行化程序的強大機制。 如需詳細資訊,請參閱 資料合約代理

您可以使用 DataContractSerializerOperationBehavior 來自訂客戶端和伺服器的序列化。 下列範例示範如何增加 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();  
Dim factory As ChannelFactory(Of IDataService) = New ChannelFactory(Of IDataService)(binding, address)  
For Each op As OperationDescription In factory.Endpoint.Contract.Operations  
        Dim dataContractBehavior As DataContractSerializerOperationBehavior = op.Behaviors.Find(Of DataContractSerializerOperationBehavior)()  
        If dataContractBehavior IsNot Nothing Then  
            dataContractBehavior.MaxItemsInObjectGraph = 100000  
        End If  
     Next  
    Dim client As IDataService = 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();  
Dim serviceHost As ServiceHost = New ServiceHost(GetType(IDataService))  
        For Each ep As ServiceEndpoint In serviceHost.Description.Endpoints  
            For Each op As OperationDescription In ep.Contract.Operations  
                Dim dataContractBehavior As DataContractSerializerOperationBehavior = op.Behaviors.Find(Of DataContractSerializerOperationBehavior)()  
  
                If dataContractBehavior IsNot Nothing Then  
                    dataContractBehavior.MaxItemsInObjectGraph = 100000  
                End If  
            Next  
        Next  
        serviceHost.Open()  

在 Web 裝載的情況下,您必須建立新的 ServiceHost 衍生類別,並使用服務主機處理站將其插入。

控制設定中的序列化設定

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>  

共用類型串行化、物件圖形保留和自定義串行化程式

DataContractSerializer 使用資料合約名稱進行串行化,而非 .NET 類型名稱。 這與服務導向的架構原則一致,而且具有極大的彈性,.NET 類型可能會變更,而不會影響連線合約。 在罕見的情況下,您可能會想要序列化實際的 .NET 類型名稱,從而在用戶端與伺服器之間引入緊密耦合,類似於 .NET Framework 遠端處理技術。 這不是建議的做法,除非在從 .NET Framework 遠端移轉至 WCF 時通常發生的罕見情況。 在此情況下,您必須使用 類別 NetDataContractSerializer ,而不是 類別 DataContractSerializer

DataContractSerializer通常會將物件圖形串行化為物件樹狀結構。 也就是說,如果同一個物件多次引用,則會多次序列化。 例如,假設實例有兩個 PurchaseOrder 類型為 Address 的欄位,稱為 billToshipTo。 如果這兩個字段都設定為相同的 Address 實例,則串行化和還原串行化之後有兩個相同的 Address 實例。 這是因為沒有標準可互通的方式表示 XML 中的物件圖形(除了在XmlSerializer上可用的舊版 SOAP 編碼標準,如在上一節StyleUse中所述)。 將物件圖形串行化為樹狀結構有某些缺點,例如,具有循環參考的圖形無法串行化。 有時候,即使無法互通,還是必須切換到真正的物件圖形串行化。 這可以透過使用建構 DataContractSerializer,並將 preserveObjectReferences 參數設定為 true 來完成。

有時候,內建串行化程式不足以用於您的案例。 在大部分情況下,您仍然可以使用 XmlObjectSerializer 抽象概念,DataContractSerializerNetDataContractSerializer 都是由其衍生的。

前三個案例(.NET 類型保留、物件圖形保留和完全自定義 XmlObjectSerializer型串行化)都需要插入自定義串行化程式。 若要這樣做,請執行下列步驟:

  1. 撰寫衍生自 DataContractSerializerOperationBehavior 的行為定义。

  2. 覆寫這兩個CreateSerializer方法,以傳回您自己的序列化程式(可以使用NetDataContractSerializer、設置為DataContractSerializerpreserveObjectReferencestrue、或您自己的自定義XmlObjectSerializer)。

  3. 開啟服務主機或建立用戶端通道之前,請先移除現有的 DataContractSerializerOperationBehavior 行為,並插入您在先前步驟中建立的自定義衍生類別。

如需進階串行化概念的詳細資訊,請參閱 串行化和還原串行化

另請參閱