WCF の検出機能

はじめに

WCF4 から追加された機能に「検出」というものがあります。これは、WCF クライアントが接続できる WCF サービスのエンドポイントアドレスを、実行時に探し出せる機能です。

簡単なサンプルで使い方を紹介

今回は構成ファイルを一切使わずに、すべてコードで記述してみます。その方が、やっている事が分かりやすいと思うので。

サービスコントラクトとサービスクラスを用意
using System.ServiceModel;

namespace DiscoverySample.Service
{
    [ServiceContract]
    public interface IGreetingService
    {
        [OperationContract]
        string Greet(string name);
    }

    public class GreetingService : IGreetingService
    {
        public string Greet(string name)
        {
            return string.Format("Hello, {0}.", name);
        }
    }
}
作成したサービスをホスト

WCF クライアントがサービスのエンドポイントを検出できるようにするには、ホスト側で設定が必要です。

using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using DiscoverySample.Service;

namespace DiscoverySample.Host
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri("net.pipe://localhost/Test");
            ServiceHost host = new ServiceHost(typeof(GreetingService), baseAddress);
            host.AddServiceEndpoint(typeof(IGreetingService),
                new NetNamedPipeBinding(),
                "Greet");

            // クライアントがエンドポイントを検出できるようにする
            host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
            host.AddServiceEndpoint(new UdpDiscoveryEndpoint());

            host.Open();

            Console.WriteLine("Enter で終了します。");
            Console.ReadLine();
            host.Close();
        }
    }
}

ServiceDiscoveryBehavior は、サービスのエンドポイントをクライアントが検出できるようにするビヘイビアです。ServiceDiscoveryBehavior は属性が用意されていないので、ServiceHost に直接突っ込んでいます。

UdpDiscoveryEndpoint は検出する際にクライアントから接続されるエンドポイントです。クライアントは、コネクションレスなプロトコルの UDP を使って、検出要求を一斉送信しているみたいです。

クライアントがエンドポイントを検出して接続
using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using DiscoverySample.Service;

namespace DiscoverySample.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // IGreetingService のエンドポイントを検出する
            FindCriteria criteria = new FindCriteria(typeof(IGreetingService));
            DiscoveryClient client = new DiscoveryClient(new UdpDiscoveryEndpoint());
            FindResponse response = client.Find(criteria);
            client.Close();
            EndpointDiscoveryMetadata metaData = response.Endpoints[0];

            // 検出したエンドポイントに接続する
            IGreetingService channel = ChannelFactory<IGreetingService>.CreateChannel(
                new NetNamedPipeBinding(),
                metaData.Address);
            string result = channel.Greet("Hayate");
            ((IClientChannel)channel).Close();

            Console.WriteLine(result);
            Console.WriteLine("Enter で終了します。");
            Console.ReadLine();
        }
    }
}

実行画面がこちら。
f:id:griefworker:20100708110746j:image

エンドポイントの検出が終わるまでに 20 秒ほど待たされます。検出にかかる時間は FindCriteria.Duration プロパティで変更できますが、短くするとエンドポイントが見つからない可能性があるので、変更しないほうがよさそうです。

まとめ

エンドポイントを検出には結構時間がかかります。また、バインディングは取得できないので、コードで指定する必要があります。バインディングも取得できればいいのに。

上記の理由から、WCF の検出機能は限られたケースでしか使えないと思います。たとえば、クライアントの通信設定を変更するような、管理用ツールとかでしょうか。そういったツールを作ろうとしている自分としては、嬉しい機能ですけどね。