いまさら、WCFで遊んでみる
だいぶ前に買って、ほったらかしていたエッセンシャルWCF:Windows Communication Foundation (Programmer’s Selection―Microsoft .net Development Series)を今更ながらに読み始めて、WCFでちょこちょこ遊んでみた。
個人的には、ローカルコンピュータ通信*1が気になってるので、その辺を中心にメモ。
とりあえず、どんなABCになるのか?
ローカルコンピュータ通信の場合、WCFのABCは大体以下のような感じじゃないかと。
Address
Adressは、ローカルコンピュータ通信の場合、何使っても大体行けるけど、名前付きパイプが、パフォーマンス的にも柔軟性も持ってるので、こいつを使うが大体の場合における最適解じゃ無いかと。
とした場合、Adressはnet.pipe://から始まる
Binding
バインディングは、有りモノを使う場合、netNamedPipeBindingになる。あんまり無いと思うけど、mexを公開する場合、そっちのバインディングはmexNamedPipeBindingになる。
Contract
これは、様々と言うことで。。。
VSデフォルトのConfigから変更する部分
何にも考えずに、WCFのサービスライブラリを作った場合、デフォルトでは、バインディングがwsHttpBindingになってる。ここを、netNamedPipeBindingへ変更して、
もし、mexを公開するなら、mexの方も、mexNamedPipeBindingに変更する。これだけで良いかというとちょっとまずい。
実際、これでデバッグ開始させて、WCFサービスホストを起ち上げると、エラーになるわけで。追加でどこを変更しなければならないかというと、ビヘイビアのserviceMetadataで、httpGetEnabledをfalseにしないとまずい。
さしあたり、Configをこのように変更することで、スケルトンで出来上がったサービスをWCFサービスホストに食わせることが可能になる。
セルフホストする場合
この手のものであれば、WASでどーこーと言うより、セルフホストする場面が多いかと思うので、セルフホストするときの手順。
ちなみに、このサンプルの場合、configは使わなかったりするw
実装として、ほめれたモンじゃないけど、さしあたりConsoleアプリでホスティングするときどうなるかというと、
- WCFのプロジェクトを参照する。
- System.ServiceModelを参照する。
が仕込み。プロジェクトを参照させただけじゃ、ホストできないので注意。
ここまでで来たら、次に、実際のコードを書いていくことになるわけで。
一番単純な方法は以下の通り。
ちなみに、以下のサンプルは、クライアントから受け取った文字列をホスト側コンソールに出力するだけの簡単なモノ
公開するメソッドをインターフェースで定義する
using System.ServiceModel; namespace SampleServiceCore { [ServiceContract] public interface ISampleService { [OperationContract(IsOneWay = true)] void SendMessage(string message); } }
公開したいInterfaceにServiceContract属性をくっつけ、同様に、公開したい操作にOperationContract属性を付ける。
実装を書く
using System; namespace SampleServiceCore { public class SampleService : ISampleService { public void SendMessage(string message) { Console.WriteLine("Message receive:" + message); } } }
ISampleServiceのInterfaceを持つクラスを作ればおっけー
ホスティングする
using System; using System.ServiceModel; using SampleServiceCore; using System; using System.ServiceModel; using SampleServiceCore; namespace Host { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(SampleService), new Uri("net.pipe://sample"))) { host.AddServiceEndpoint(typeof(ISampleService), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), string.Empty); host.Open(); Console.WriteLine("press enter to exit..."); Console.ReadLine(); } } } }
今回は、mexを公開しない方向で。
ここで注意するのは、ServiceHostのコンストラクタ。
2つのオーバーロードがあるけど、今回使ったのは、サービスを実装しているクラスのTypeを渡す方。
もう一方のObjectを渡す方は、シングルトンで使うコトが出来るけど、その場合、SampleServiceに仕込みを入れる必要がある。また、仕込みを入れないで、自分でインスタンスを先に作成して、Ojbectを渡す方を使った場合、実行時エラーになるので注意。
クライアントを作って実際に呼び出してみる
で、最後は、クライアントの作成。
クライアントアプリケーションにも、ホストと同様に、件のInterfaceとその実装が入ってるアセンブリと、System.ServiceModelを参照させる。
そこまでしたら、あと一息、実装は以下
using System.ServiceModel; using SampleServiceCore; namespace Client { class Program { static void Main(string[] args) { using (ChannelFactory<ISampleService> factory = new ChannelFactory<ISampleService> (new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://sample")) { var client = factory.CreateChannel(); client.SendMessage("Hello WCF!"); var ret = client.GetData(100); } } } }
ここまで言ったら、コンパイルして、ホストプロセスを起ち上げ、その後に、クライアントプロセスを起動すれば、ホスト側コンソールに、
”Hello WCF!”が出てくるハズ。
注意すべきトコロ
当たり前だけど、Host側エンドポイントをClientで正確に記述する必要がある。
また、NetNamedPipeBindigのコンストラクタで、セキュリティモードを指定できるけど、それもホスト/クライアントで一致させないとまずい。
次回は、同期呼び出しの戻り値を持つ操作とプリミティブ意外のデータをやりとりする方法当たりを書いてみようかと思います。