対象読者
本記事はC#でのプログラミングを行ったことがある方を対象としています。サンプルを動作させるための環境設定等は「WPF(Windows Presentation Foundation)+XAML入門 前編」ご覧ください。
関連記事
WCF登場の歴史的経緯
WCF(コードネーム:Indigo)は、.NET Framework 3.0の通信部分を司るコンポーネントです。さて、WCFについて考える前に、これまで存在したネットワーク分散サービスのフレームワークをいくつか列挙してみましょう。
フレームワーク | 概要 |
ASP.NET Web Services (ASMX) | ASP.NETを使用したXML Web Serviceのフレームワーク |
Web Service Enhancements (WSE) 拡張 | Webサービス拡張仕様(WS-*)をサポートするフレームワーク |
Microsoft メッセージ キュー (MSMQ) | Microsoft Message Queuingを使用するフレームワーク |
.NET Remoting | .NETコンポーネント間で使用可能な通信方式 |
WCFが登場したのは、これらのフレームワークに不足があり、完全に新しい通信方式が必要になったため、というわけではありません。しかしこれらのフレームワークは、基本的にそれ単体で使用することが想定された排他的なものであり、複数の通信方式の混在している等の理由から、他のフレームワークへの移行が難しいという問題がありました。
例えば、ASMXを使用していたアプリケーションを、パフォーマンス向上のために.NET Remotingを使用するものに変更しようとする場合、それらのフレームワークには互換性がないため、多くのコードの書き換えが必要となりました。従って通信テクノロジとして、どのフレームワークを使用するかという決定はプロジェクトの初期で行い、以後は他のフレームワークへの移行を考慮せず、特定のフレームワーク専用のコーディングを行うのが普通でした。
WCFを使えば、こうしたフレームワーク選択にかかわる問題が無くなり、いずれの通信方式を用いるにしても共通のアプローチで実装を進めることができます。
WCFで使用可能な通信方式
WCFでは、以下の通信方式を使用することができます。
通信方式 | 概要 | 相互運用性 |
TCP | TCP/IPを使用する通信方式 | WCFコンポーネント |
HTTP | Webサービスに準拠した通信方式。Basic-ProfileないしはWebサービス拡張仕様をサポートする | Webサービスをサポートするアプリケーション |
名前付きパイプ | 名前付きパイプを使用する通信方式 | 同一マシン場のWCFコンポーネント |
MSMQ | Microsoft Message Queuingを使用する通信方式 | WCFコンポーネント/MSMQをサポートするアプリケーション |
Peer to Peer | Windows Peer-to-Peer Networkingを使用する通信方式 | WCFコンポーネント |
他にも独自の通信方式を自分で定義し、WCFに組み込むことも可能です。今回のサンプルではHTTPおよびTCPを使用し、ほぼ同一のコードでどのように通信方式を切り替えることができるかを確認します。
キーワードは「ABC」
WCFの重要な概念となるのが「ABC」です。これは3つのキーワードの頭文字から来ています。
頭文字 | キーワード | 意味 |
A | Address | サービスを提供する場所 |
B | Binding | サービスで使用するバインディング |
C | Contract | サービスの内容 |
それぞれのキーワードについて説明しましょう。
「A」:Address(アドレス)
アドレスは「サービスをどの場所で提供するか」を定義するもので、URI形式で表現します。その中に含める要素は
- 使用するトランスポートプロトコル
- 使用するホスト名
- (必要な場合)ポート番号
- 提供するパス
です。以下にいくつかの例を示します。
使用する通信方式 | URI |
HTTP | http://somehost:8080/BasicService |
TCP | net.tcp://somehost:8081/TcpService |
名前付きパイプ | net.pipe://somehost/NamedPipeService |
通常はポート番号を指定しますが、名前付きパイプとMSMQではポート番号の指定は不要です。
「B」:Binding(バインディング)
バインディングは「サービスをどのような形でネットワークに公開するか」を定義するものです。
例えば、通信方式としてHTTPを使用する場合でも、Basic-Profileに準拠する場合とWebサービス拡張仕様に準拠する場合では異なるバインディングを使用します。
以下に標準でサポートされているバインディングのいくつかを示します。
バインディング名 | 概要 |
NetTcpBinding | TCP/IPを使用するバインディング |
BasicHttpBinding | WebサービスのBasic-Profileを使用するバインディング |
WsHttpBinding | Webサービス拡張仕様を使用するバインディング |
NetNamedPipeBinding | 名前付きパイプを使用するバインディング |
NetMsmqBinding | MSMQを使用してWCFコンポーネントと接続するためのバインディング |
MsmqIntegrationBinding | MSMQを使用し、WCFコンポーネントでないMSMQアプリケーションと接続するためのバインディング |
以下に各バインディングの特徴をふまえ、選択の指針を示します。
- Webサービスとして公開する必要がある場合はHTTP系のバインディングを選択し、必要な相互運用レベルに合わせてBasicHttpBindingあるいはWsHttpBindingを選択しましょう。これらのバインディングはすべてテキストベースでの通信が行われ、WCF以外のフレームワークとも通信が可能です。
- WCFサービス以外との通信が必要ない場合は、NetTcpBinding、NetNamedPipeBindingなどのバイナリベースでの通信を検討できます。同一マシン間であればNetNamedPipeBindingが高速で、それ以外の場合はNetTcpBindingを使用します。
- メッセージ・キューの細かな制御が必要な場合はMSMQを使用するバインディングを検討してください。
前編では1番シンプルなBasicHttpBindingで動作を確認し、後編にてNetTcpBindingへと切り替えてみます。
「C」:Contract(コントラクト)
コントラクトは「どんなサービスが提供されるか」を定義するものです。WCFのコントラクトは、サービスを提供するクラス・インターフェイスの定義であるサービス・コントラクトと、やり取りするメッセージについての定義をするコントラクトで構成されています。メッセージについてのコントラクトにはいくつか種類があります。
コントラクト | 概要 |
プリミティブ型 | 数値・文字列などの.NETの基本データ型は特定の属性を付加することなく、そのまま使用可能 |
Messageクラス | メッセージについての詳細を定義せず、汎用のMessageクラスを使用する方法 |
データ・コントラクト | クラス・インターフェイスのメソッド・プロパティについて属性を付加することでやり取りするデータを指定する方法 |
メッセージ・コントラクト | WCFを介してやり取りするSOAPメッセージの構造を直接定義する方法 |
今回は、自作のデータクラスにデータ・コントラクトを定義して使用します。
最初にサービス・コントラクトを定義しましょう。例えば、WCFサービスを使って会員情報を他のアプリケーションに提供する場合に、次のようなメソッドをサービスとして公開するとします。
- 会員情報を追加するサービス
- 会員IDから会員情報を取得するサービス
これらは.NETのインターフェイスとして次のように定義されます。
[ServiceContract()] public interface ISampleService { [OperationContract] void addMember(Member member); [OperationContract] Member getMember(int memberId); }
まず、サービスを提供するクラスであるISampleService
インターフェイスにServiceContract
属性が、そして会員情報を追加するaddMember
メソッドおよび取得するgetMember
メソッドそれぞれにOperationContract
属性が付加されています。これらはSystem.ServiceModel名前空間で定義されている属性で、サービスをネットワーク上に公開することを表します。
それぞれの属性の代表的なプロパティを記します。
プロパティ名 | 概要 |
ConfigurationName | WCFサービスの構成名。デフォルトではクラス/インターフェイス名が使用される |
Name | WCFサービスのメタデータをWSDLとして公開する場合のportType要素のname属性 |
NameSpace | WCFサービスのメタデータをWSDLとして公開する場合のportType要素の名前空間 |
SessionMode | 複数のEndpoint間でのセッションを使用するかどうか |
プロパティ名 | 概要 |
Action | メソッドを公開する名前。デフォルトではメソッド名がそのまま使用される |
IsOneWay | メソッドが値を返さないか。このプロパティがtrueのメソッドを呼び出した場合、クライアントはメソッドを呼び出した時点で、メソッドの処理完了を待たずに次の処理に移行する |
IsInitiating | メソッド呼び出しによってセッションが開始するかどうか。このプロパティがfalseのメソッドの呼び出しは、このプロパティがtrueのメソッドを呼び出した後(=セッションが開始した後)でなければ呼び出すことができない |
IsTerminating | メソッド呼び出しによってセッションが終了するかどうか |
続いて、メッセージについてのコントラクトを定義します。このサービスで使用するデータである会員情報についてのコントラクトを見てみましょう。
[DataContract] public class Member { int _id; string _name; [DataMember] public int Id { get { return _id; } set { _id = value; } } [DataMember] public string Name { get { return _name; } set { _name = value; } } }
ここでは、会員情報を表すMember
クラスにDataContract
属性が、そして会員IDを表すId
プロパティおよび会員名を表すName
プロパティにDataMember
属性が付加されています。これらの属性はSystem.Runtime.Serialization名前空間で定義されており、ネットワークにシリアライズされる、つまりデータがネットワーク上を通過可能することを表します。
WCFサービスを提供する側とWCFサービスを使用する側では、これらのコントラクトを共有することで、相互に通信を行うことができます。
ただ、サービス実装クラスをクライアントが持つ必要はなく、コントラクトはサービス提供側とクライアント側双方で共有する情報であることからすれば、通常はインターフェイスに対して属性を付加するのが望ましいでしょう。
「ABC」のまとめ
WCFの「ABC」は、「どこで(Address)・どのように(Binding)・何を(Contract)」提供するかを定義しています。サービス提供側とクライアント側双方で同じ「ABC」を使用することで、齟齬のない通信を行うことができます。これらの「ABC」をまとめてEndpointと呼びます。サービスを提供/使用するためのソケットのようなイメージです。
WCFサービスは複数のEndpointを持つことができます。つまり、1つのWCFサービス中に、複数の通信方式を持つEndpointを持たせることができるわけです。これもWCFの特徴と言えます。
さて、Endpointを構成する「ABC」のうち、アドレスとバインディングは使用する通信方式に依存していますが、コントラクトは通信方式に依存していません。
従って、使用する通信方式を切り替える場合には、サービス提供側とクライアント側双方でアドレスとバインディングを切り替える、つまり別のEndpointを使用すれば、同じコントラクトを使って通信を行うことができます。
実装における「ABC」
では早速実装に……入る前に、もう少し確認しておきましょう。
「ABC」のうち、C(コントラクト)は.NETのクラス・インターフェイスに付加された属性で定義することは既に確認しました。では、A(アドレス)とB(バインディング)はどのように定義するのでしょうか。
WCFではソースコードによる「ABC」指定とXMLによる「ABC」指定の両方を使用することができます。今回はXML形式の設定ファイルでの指定方法を示します。
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <services> <service name="WCFMemberServiceLibrary.SampleService"> <endpoint address="http://localhost:8080/MemberService"
binding="basicHttpBinding"
contract="WCFMemberServiceLibrary.ISampleService"
listenUriMode="Explicit"> </endpoint> </service> </services> </system.serviceModel> </configuration>
XMLによる「ABC」定義は、.NET標準の構成ファイル(Windowsアプリケーションでは「app.config」、Webアプリケーションの場合は「web.config」)を使用します。そして、endpoint
要素のaddress
、binding
、contract
という3属性で「ABC」を定義します。
address
属性はサービスを提供するURIを、binding
属性は使用するバインディングを、contract
属性はサービス・コントラクトを定義したクラス・インターフェイスを、それぞれ指定します。
listenUriMode
属性は公開するアドレスを明示的に指定するか、一意になるように自動生成するかを指定する属性です。デフォルトの値であるExplicitではaddress
属性の値がそのまま使用されます。値としてUniqueを設定すると、アドレスの末尾にこのサービスについての一意な値であるGUIDが付加され、アドレスの衝突を避けてくれます。また、NetTcpBindingの場合は未使用のポート番号も自動的に選択してくれます。
ただ、このXMLを手動で編集するのは煩雑であるため、Visual Studio 2005 extensions for WCFで「Microsoft Service Configuration Editor」というソフトウェアが提供されており、GUIでこれらの定義を編集することができます。詳しくは後述します。
クライアント側の「ABC」実装
もう1つだけ確認事項です。サービス提供側で定義した「ABC」ですが、クライアント側ではどのように共有すればよいのでしょうか。
まず、サービス提供側の構成ファイル(app.config、web.config)をクライアント側のMicrosoft Service Configuration Editorで読み込んで必要な情報を生成する、という方法があります。この場合、app.configの生成と同時に、サービス提供側で作成したコントラクトのソースコードをクライアント側にコピーして共有する必要があります。
もう1つは、通信方式としてWebサービスを使用する場合は、Webサービスを介してコントラクトについてのメタデータを取得することで、「ABC」を共有することができます。今回は両方の方式をサンプルとして示します。