WCFを使うワークフロー

WCFは終わった技術だけど、Windows環境で機能を分けたいときは便利なので、やりかたをまとめておく。

ソリューションの作成

まずは空のプロジェクトを作る。

WCF Service Libraryプロジェクトを追加

再利用を考えて、必要な機能をService Libraryとして作成する。CalcServiceLibraryとしてプロジェクトを作成。IService1.cs,Service1が勝手にできるので、これから作るサービス名にそれぞれリネームする。ここではICalcService,CalcServiceとする。

[ServiceContruct]
public interface ICalcService
{
  [OperationContract]
  int Add(MyObj obj);
  [OperationContract(IsOneWay = true)]
  void Log(string message);
}
[DataContract]
public class MyObj
{
  [DataMember]
  int Number1 {get; set;}
  [DataMember]
  int Number2 {get; set;}
}
public CalcService : ICalcService
{
  //インターフェースを実装
}

後はこのプロジェクト部分をコピペで持っていけば、いろんなソリューションで使える。このLibraryをスタートプロジェクトに設定して実行すると、WCFHostと簡易クライアントが動くので、簡単なデバッグはこれだけでできる。

ホストを選ぶ

ホストは

  • SelfHost
  • WEB(IIS)
  • Windows Service

から選べる。何となくだけど、SelfHostで作って試して、後は必要に応じてホストを変えればいいんじゃないかと思う。バインディングのスピード的にはNamePipeが一番速いという話らしい。

ホスト(SelfHost)の場合

プロジェクトにConsoleアプリケーションを追加。サービス参照でSystem.Runtime.Serialization,System.ServiceModel,System.ServiceModel.Web,そしてCalcServiceLibraryを追加する。

using (var servicehost = new ServiceHost(typeof(CalcService)))
{
  ServiceEndpoint BasicHttpEndPoint = servicehost.AddServiceEndpoint(
    typeof(ICalcService),
    new BasicHttpBinding(),
    "http://localhost:10000/CalcService");
  ServiceEndpoint NettcpEndPoint = servicehost.AddServiceEndpoint(
    typeof(ICalcService),
    new NetTcpBinding(),
    "net.tcp:/localhost:10100/CalcService");
  
  servicehost.Open();
  Console.WriteLine("Now Service is running...");
  Console.ReadLine();
  
  servicehost.Close();
}

エンドポイント条件をプログラム中に書かないでApp.Configに記載する場合は以下

using (var servicehost = new ServiceHost(typeof(CalcService)))
{  
  servicehost.Open();
  Console.WriteLine("Now Service is running...");
  Console.ReadLine();
  
  servicehost.Close();
}
</startup>
<system.serviceModel>
  <services>
    <service name="CalcServiceLibrary.CalcService>
      <endpoint address="http://localhost:10000/CalcService"
        binding="basicHttpBinding"
        bindingConfiguration=""
        name="CalcService_BasicHttp"
        contract="CalcServiceLibrary.ICalcService"/>
      <endpoint address="net.tcp://localhost:10100/CalcService"
        binding="NetTcpBinding"
        bindingConfiguration=""
        name="CalcService_NetTcp"
        contract="CalcServiceLibrary.ICalcService"/>
    </serivce>
  </services>
</system.serviceModel>

実はApp.configを右クリするとSerivceEndpointをGUIで編集出来たりする。

ホスト(WEB)の場合

プロジェクトにWCF Service Applicationを追加。追加されるIService1.csは削除。Service1.svcはコードビハインドのService1.svc.csを削除。そしてService1.svcをCalcService.scvにリネームする。「参照の設定」でCalcServiceLibraryを追加。そしてCalcService.scvの中身を書き換え。

<%@ ServiceHost Language="C#" Debug="true" Service="CalcServiceLibrary.CalcService" %>

Web.Configに追記。

  <services>
    <service name="CalcServiceLibrary.CalcService">
      <endpoint address="" binding="basicHttpBinding" contract="CalcServiceLibrary.ICalcService"/>
    </service>
  </services>

Web.configを右クリ->WCF構成の編集、でも可能。

あとはCalcService.scvを右クリ->ブラウザーで表示でサービスが起動する。

Host(Windows Service)の場合

一番スマートっぽい感じになるのがこれ。けど、インストールになっちゃうのとデバッグはしづらいというか単純にはできないから、完成後に。

プロジェクトに[Windows Service]のテンプレートからプロジェクト作成。参照でSystem.Runtime.Serialization,System.ServiceModel,System.ServiceModel.Web,そしてCalcServiceLibraryを追加する。

App.configに記載する内容はホスト(SelfHost)と一緒。

Service1.csを適当にリネーム(WSCalcService.cs)する。そしてコードビューを表示。

private ServiceHost host = null;

protected override void OnStart(string[] args)
{
  host = new ServiceHost(typeof(CalcService));
  host.Open();
}

protected override void OnStop()
{
  if (host != null)
  {
    host.Close();
  }
  host = null;
}

ソリューションエクスプローラのWSCalcService.csをダブルクリックするとデザインビューが表示されるので、その表示ペインで右クリックコンテキストメニューで「インストーラーの追加」を選択する。

serviceProcessInstaller1とserviceInstaller1が表示されるので、serviceProcessInstaller1を右クリックしてプロパティを表示。AccountをLocalSystemに設定。

次にserviceInstaller1のプロパティを表示。Display Name, ServiceNameを適当につけて、StartTypeをAutomaticにする。で、リビルドしてEXEを作成。

コマンドプロンプトで

installutil WSServiceHost.exe

でインストールできればOK。services.mscを利用してサービスを動作させたりする。

簡易クライアントを作る

App.configに書く内容はホストを作るときと一緒。

実際に使うところでは

ICalcService basicHttpChannel = new ChannelFactory<CalcServiceLibrary.ICalcService>
  ("CalcService_BasicHttp").CreateChannel();
var s = new MyObj() {Number1 = 1, Number2 = 2};
var result = basicHttpChannel.Add(s);
Console.WriteLine(result.ToString());  //3