かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

今時のWCFの構成ファイルは地獄じゃない

WCFは、素晴らしいテクノロジ!!だけど構成ファイルがだるい・・・。1サービスあたりweb.configやapp.configにserviceタグ追加って正気の沙汰じゃない!と思ってましたが、最近はそうでもないみたいですね。
WCF サービスの簡略化された構成
ここで、重要なのがプロトコル単位で一括設定できるところかなと思います!ということで、まぁやってみようと思います。
管理者権限でVS2012 RCをおも室に立ち上げます。コンソールアプリケーション作って参照設定にSystem.ServiceModelを追加します。

そして、以下のような簡単なサービス用のクラスを定義します。

[ServiceContract]
public interface ICalc
{
    [OperationContract]
    int Add(int x, int y);
}

public class Calc : ICalc
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

前までは、コードで構成するか構成ファイルで1サービス、1サービス定義するかしないといけなかったのですが、簡略化された構成だとこんな感じでいけます。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <protocolMapping>
      <!-- httpのプロトコルのものに一括設定 -->
      <remove scheme="http" />
      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="httpBindingConfig" />
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <!-- basicHttpBindingの設定内容 -->
        <binding name="httpBindingConfig" closeTimeout="00:02:00" openTimeout="00:02:00"
            sendTimeout="00:02:00" maxBufferPoolSize="2000000" maxBufferSize="2000000"
            maxReceivedMessageSize="2000000" messageEncoding="Mtom">
          <readerQuotas maxDepth="2000000" maxStringContentLength="2000000"
              maxArrayLength="2000000" maxBytesPerRead="2000000" maxNameTableCharCount="2000000" />
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

これだけみると、結構構成大変そうですがhttpでサービスの公開をする限りは、一括でこの設定が適用されるため構成いらず・・・!素晴らしい。
ServiceHost使ってサービスをホストするお決まりコードを書きます。

// サービスを公開するコードはこれだけ
var host = new ServiceHost(
    typeof(Calc),
    new Uri("http://localhost:8080/Calc"));
host.Open();


Console.ReadLine();
host.Close();

次にクライアント側の構成を書きましょう。普通は別アプリになるのですが、ここでは同じアプリ内に書いちゃいます。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <!-- クライアント側の設定は簡単にはできないのかな… -->
    <client>
      <endpoint binding="basicHttpBinding" bindingConfiguration="httpBindingConfig"
        contract="WcfEducation.ICalc" name="calcClientEndpoint" address="http://localhost:8080/Calc">
      </endpoint>
    </client>
    <protocolMapping>
      <!-- httpのプロトコルのものに一括設定 -->
      <remove scheme="http" />
      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="httpBindingConfig" />
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <!-- basicHttpBindingの設定内容 -->
        <binding name="httpBindingConfig" closeTimeout="00:02:00" openTimeout="00:02:00"
            sendTimeout="00:02:00" maxBufferPoolSize="2000000" maxBufferSize="2000000"
            maxReceivedMessageSize="2000000" messageEncoding="Mtom">
          <readerQuotas maxDepth="2000000" maxStringContentLength="2000000"
              maxArrayLength="2000000" maxBytesPerRead="2000000" maxNameTableCharCount="2000000" />
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

んで、サービス呼び出しのコードをServiceHostのOpenとCloseの間に書きます。

// サービスを公開するコードはこれだけ
var host = new ServiceHost(
    typeof(Calc),
    new Uri("http://localhost:8080/Calc"));
host.Open();

// クライアントを作ってサービスを呼び出して閉じる
var channel = new ChannelFactory<ICalc>("calcClientEndpoint");
var client = channel.CreateChannel();

Console.WriteLine("10 + 2 = {0}", client.Add(10, 2));
            
channel.Close();

Console.ReadLine();
host.Close();

実行するとちゃんと結果でます。

10 + 2 = 12

というわけで、簡略化された構成使うとWCFもあまりめんどくさくないということでした。いいことですね。