サービス コントラクトでのデータ転送の指定
Windows Communication Foundation (WCF) は、一種のメッセージング インフラストラクチャと考えることができます。サービス操作では、メッセージを受信し、それらのメッセージを処理し、送信することができます。メッセージは、操作コントラクトを使用して記述されます。たとえば、次のようなコントラクトがあるとします。
[ServiceContract]
public interface IAirfareQuoteService
{
[OperationContract]
float GetAirfare(string fromCity, string toCity);
}
この場合、GetAirfare
操作では、fromCity
と toCity
に関する情報を含むメッセージを受け入れ、数値を含むメッセージを返します。
ここでは、操作コントラクトでメッセージを記述するさまざまな方法について説明します。
パラメーターを使用したメッセージの記述
メッセージを記述する最も簡単な方法は、パラメーター リストと戻り値を使用することです。前の例では、fromCity
と toCity
の 2 つの文字列パラメーターを使用して要求メッセージを記述し、float 型の戻り値を使用して応答メッセージを記述しました。戻り値だけでは応答メッセージを記述できない場合は、out パラメーターを使用できます。たとえば、次の操作は、fromCity
と toCity
を要求メッセージに含め、数値と通貨を応答メッセージに含めます。
[OperationContract]
float GetAirfare(string fromCity, string toCity, out string currency);
また、参照パラメーターを使用すると、要求メッセージと応答メッセージの両方のパラメーター部分を作成できます。パラメーターは、シリアル化 (XML への変換) が可能な型にする必要があります。既定では、WCF は、DataContractSerializer クラスというコンポーネントを使用してこの変換を実行します。ほとんどのプリミティブ型 (int、string、float、DateTime など) がサポートされます。ユーザー定義型には、通常、データ コントラクトが存在する必要があります。詳細については、次のトピックを参照してください。 データ コントラクトの使用.
public interface IAirfareQuoteService
{
[OperationContract]
float GetAirfare(Itinerary itinerary, DateTime date);
}
[DataContract]
public class Itinerary
{
[DataMember]
public string fromCity;
[DataMember]
public string toCity;
}
使用している型のシリアル化に 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;
}
詳細については、次のトピックを参照してください。 XmlSerializer クラスの使用.「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
操作は何も返しません。この操作からエラー状態を伝達することはできません。
メッセージ コントラクトを使用したメッセージの記述
1 つの型を使用してメッセージ全体を表現する場合があります。この場合、データ コントラクトを使用することもできますが、メッセージ コントラクトを使用する方法をお勧めします。この方法を使用すると、結果の 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 は、シリアライザーを呼び出す代わりに、ストリームからデータを取得して送信メッセージに直接配置するか、受信メッセージからデータを取得してストリームに直接配置します。次に、ストリームの使用例を示します。
[OperationContract]
public Stream DownloadFile(string fileName);
1 つのメッセージ本文で 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);
これは高度なシナリオです。詳細については、「メッセージ クラスの使用」を参照してください。
エラー メッセージの記述
一方向ではない操作では、戻り値と出力パラメーターまたは参照パラメーターによって記述されるメッセージに加え、通常の応答メッセージとエラー メッセージの少なくとも 2 つのメッセージを返すことができます。たとえば、次の操作コントラクトがあるとします。
[OperationContract]
float GetAirfare(string fromCity, string toCity, DateTime date);
この操作は、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;
}
これらの追加エラーは、適切なデータ コントラクト型の FaultException をスローすることによって生成できます。詳細については、次のトピックを参照してください。 例外とエラーの処理.
XmlSerializer クラスを使用してエラーを記述することはできません。XmlSerializerFormatAttribute は、エラー コントラクトに影響を及ぼしません。
派生型の使用
操作コントラクトやメッセージ コントラクトで基本型を使用し、その後、実際に操作を呼び出すときに派生型を使用する場合があります。この場合、ServiceKnownTypeAttribute 属性または何らかの代替機構を使用して、派生型を使用できるようにする必要があります。次の操作があるとします。
[OperationContract]
public bool IsLibraryItemAvailable(LibraryItem item);
LibraryItem
から派生する Book
と Magazine
の 2 つの型があると想定します。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 属性と同様に、型を受け取るか、既知の型の一覧を取得するために呼び出すメソッドの名前を受け取ります。詳細については、次のトピックを参照してください。 既知のデータ コントラクト型.
Use と Style の指定
Web サービス記述言語 (WSDL: Web Services Description Language) を使用してサービスを記述するときは、一般にドキュメントとリモート プロシージャ コール (RPC: Remote Procedure Call) の 2 つのスタイルが使用されます。ドキュメント スタイルでは、スキーマを使用してメッセージ本文全体が記述されます。WSDL では、該当するスキーマの中の要素を参照して、メッセージ本文のさまざまな部分を記述します。RPC スタイルでは、WSDL は、要素ではなく、メッセージの各部を表すスキーマ型を参照します。場合によっては、これらのスタイルのいずれかを手動で選択することが必要になります。これを行うには、DataContractFormatAttribute 属性を適用し、Style プロパティを設定するか (DataContractSerializer を使用している場合)、XmlSerializerFormatAttribute 属性の Style を設定します (XmlSerializer を使用している場合)。
また、XmlSerializer は、シリアル化された XML の形式として、Literal と Encoded の 2 つの形式をサポートしています。Literal は、最も一般的に受け入れられている形式であり、DataContractSerializer がサポートする唯一の形式です。Encoded は、SOAP 仕様のセクション 5 に記載されているレガシ形式であるため、新しいサービスにはお勧めしません。Encoded モードに切り替えるには、XmlSerializerFormatAttribute 属性の Use プロパティを Encoded に設定します。
ほとんどの場合、Style プロパティと Use プロパティの既定の設定は変更しないでください。
シリアル化プロセスの制御
データをシリアル化する方法をカスタマイズする場合、さまざまな設定を行うことができます。
サーバーのシリアル化設定の変更
既定の DataContractSerializer を使用している場合は、ServiceBehaviorAttribute 属性をサービスに適用することで、サービスでのシリアル化プロセスの一部の側面を制御できます。具体的には、MaxItemsInObjectGraph プロパティを使用して、DataContractSerializer が逆シリアル化するオブジェクトの最大数を制限するクォータを設定できます。また、IgnoreExtensionDataObject プロパティを使用すると、ラウンド トリップ バージョン管理機能を無効にできます。クォータ詳細情報、「セキュリティに関するデータの考慮事項」を参照してください。ラウンド トリップ詳細情報、「上位互換性のあるデータ コントラクト」を参照してください。
[ServiceContract]
[ServiceBehavior(MaxItemsInObjectGraph=100000)]
public interface IDataService
{
[OperationContract] DataPoint[] GetData();
}
シリアル化の動作
WCF には、特定の操作で使用しているシリアライザーに応じて自動的にプラグインされる、DataContractSerializerOperationBehavior と XmlSerializerOperationBehavior の 2 つの動作が用意されています。これらの動作は自動的に適用されるため、通常、これらの動作を意識する必要はありません。
ただし、DataContractSerializerOperationBehavior には、MaxItemsInObjectGraph、IgnoreExtensionDataObject、および DataContractSurrogate の 3 つのプロパティがあり、これらを使用してシリアル化プロセスをカスタマイズできます。最初の 2 つのプロパティの意味は、前のセクションの説明と同じです。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();
自己ホスト型サービスでの同等のコードを次に示します。
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();
Web ホスト型の場合は、新しい ServiceHost 派生クラスを作成し、サービス ホスト ファクトリを使用してプラグインする必要があります。
構成でのシリアル化設定の制御
次の例に示すように、MaxItemsInObjectGraph と IgnoreExtensionDataObject は、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 への移行時に発生するまれなケースを除き、これはお勧めしません。この場合、DataContractSerializer クラスではなく、NetDataContractSerializer クラスを使用する必要があります。
DataContractSerializer は、通常、オブジェクト グラフをオブジェクト ツリーとしてシリアル化します。つまり、同じオブジェクトが複数回にわたって参照される場合、そのオブジェクトは複数回にわたってシリアル化されます。たとえば、billTo および shipTo という、Address 型の 2 つのフィールドを持つ PurchaseOrder インスタンスがあるとします。この両方のフィールドに同じ Address インスタンスを設定すると、シリアル化および逆シリアル化の後に 2 つの同一の Address インスタンスが生じます。これは、オブジェクト グラフを XML で表現する相互運用可能な標準の方法がないためです (ただし、Style と Use に関する前のセクションで説明した XmlSerializer で使用できる従来の SOAP エンコード標準を除きます)。オブジェクト グラフをツリーとしてシリアル化する場合、循環参照が含まれたグラフをシリアル化できないなどの欠点があります。場合によっては、相互運用が可能ではありませんが、真のオブジェクト グラフ シリアル化に切り替えることが必要になります。これを行うには、preserveObjectReferences パラメーターを true に設定して構築した DataContractSerializer を使用します。
シナリオによっては、組み込みのシリアライザーでは不十分な場合があります。ただし、ほとんどの場合、DataContractSerializer と NetDataContractSerializer の派生元である XmlObjectSerializer 抽象クラスを使用できます。
前述の 3 つのケース (.NET 型の保存、オブジェクト グラフの保存、および XmlObjectSerializer ベースの完全なカスタム シリアル化) では、すべてのカスタム シリアライザーをプラグインする必要があります。これを行うには、次の手順を実行します。
DataContractSerializerOperationBehavior から派生する独自の動作を記述します。
独自のシリアライザー (NetDataContractSerializer、preserveObjectReferences が true に設定された DataContractSerializer、独自のカスタム XmlObjectSerializer のいずれか) を返すように 2 つの CreateSerializer メソッドをオーバーライドします。
サービス ホストを開いたり、クライアント チャネルを作成したりする前に、既存の DataContractSerializerOperationBehavior 動作を削除し、前の手順で作成したカスタム派生クラスをプラグインします。
シリアル化の高度な概念詳細情報、「シリアル化と逆シリアル化」を参照してください。
参照
処理手順
方法 : ストリーミングを有効にする
方法 : クラスまたは構造体に基本的なデータ コントラクトを作成する