コードブロックを受け取る WCF クライアント

WCF では、クライアントがサービスを呼び出すとき、チャネルを開閉するコードを書く必要があります。サービスを呼び出す度に同じコードを書くのは面倒です。

この問題を解決する方法として、以前、RealProxy を使って動的に WCF クライアントを生成するサンプルを紹介しました。

しかし、コードブロックを上手く利用すれば、わざわざ RealProxy を使う必要はないかもしれません。

例えば、次のようなクラスを用意します。

public class ChannelProvider<TChannel> where TChannel : class
{
    private Binding _binding;
    private string _endpointAddress;

    public ChannelProvider(Binding binding, string address)
    {
        _binding = binding;
        _endpointAddress = address;
    }

    // Channel を生成
    public TChannel CreateChannel()
    {
        return ChannelFactory<TChannel>
            .CreateChannel(_binding, new EndpointAddress(_endpointAddress));
    }

    // 結果を返さないメソッドを呼び出す
    public void Invoke(Action<TChannel> block)
    {
        TChannel channel = CreateChannel();
        block(channel);
        ((IChannel)channel).Close();
    }

    // 結果を返すメソッドを呼び出す
    public TResult Result<TResult>(Func<TChannel, TResult> block)
    {
        TChannel channel = CreateChannel();
        TResult result = block(channel);
        ((IChannel)channel).Close();
        return result;
    }
}

このクラスを使って、次のようにサービスを呼び出します。

[ServiceContract]
public interface IGreetingService
{
    [OperationContract]
    string Greet(string name);
}

public class GreetingService : IGreetingService
{
    public string Greet(string name)
    {
        return string.Format("Hello, {0}!", name);
    }
}

class Program
{
    static void Main(string[] args)
    {
        // サービス開始
        string address = "net.pipe://localhost/Greeting";
        ServiceHost host = new ServiceHost(typeof(GreetingService));
        host.AddServiceEndpoint(typeof(IGreetingService),
            new NetNamedPipeBinding(),
            address);
        host.Open();

        ChannelProvider<IGreetingService> provider = new ChannelProvider<IGreetingService>(
            new NetNamedPipeBinding(),
            address);

        // チャネルを使って処理を行うブロックを、Result メソッドの引数に渡す
        string result = provider.Result<string>(p => p.Greet("Ichiro"));

        Console.WriteLine(result);
        Console.ReadLine();

        // サービス終了
        host.Close();
    }
}

コードブロックを渡すので、チャネルを開いてから閉じる間に、複数回サービスを呼び出せます。それに対し、RealProxy を使った方法だと、サービスを呼び出す度にチャネルを開閉します。

RealProxy を使った方法よりも、今回の方法のほうが効率がいいですね。