クライアントからコンテキストを送信する

今まではクライアントの情報をサービスに引数で渡してました

プロジェクト ID やクライアント ID といったクライアントの情報を、サービスに引数として渡していました。でもこの方法は全然洗練されていません。引数が増えてしまう。できればクライアント情報を渡す部分は隠ぺいしたい。

コンテキスト付きバインディングを使えば引数で渡さなくていい!

.NET Framework 3.5 で追加されたコンテキスト付きバインディングを使えば、任意のクライアントの情報をメッセージのヘッダに埋め込むことができます。引数で渡す必要が無くなる!
コンテキスト付きバインディングには、BasicHttpContextBinding, NetTcpContextBinding, WSHttpContextBinding があります。これらは System.Workflow.Services.dll アセンブリに含まれています。

クライアント側はIContextManagerを使ってコンテキストを渡す

ISampleService proxy = ChannelFactory<ISampleService>.CreateChannel(
    new NetTcpContextBinding(),
    new EndpointAddress("net.tcp://localhost:8000/SampleService"));

IContextManager manager = ((IChannel)proxy).GetProperty<IContextManager>();
IDictionary<string, string> context = manager.GetContext();
context["ProjectId"] = "12345";
context["ClientId"] = "sample";
manager.SetContext(context);

proxy.Execute();

ここで1つ注意点。コンテキストはプロキシを開く(またはそれを初めて使用する)前に1回だけ設定できます。その後は辞書がキャッシュされ、それを変更しようとするとエラーになります。

サービス側はContextMessagePropertyを使ってコンテキストを取り出す

public void Execute()
{
    ContextMessageProperty props = OperationContext.Current.IncomingMessageProperties[ContextMessageProperty.Name]
                                   as ContextMessageProperty;
    string projectId = props.Context["ProjectId"];
    string clientId = props.Context["ClientId"];

    Console.WriteLine("ProjectId:{0}, ClientId:{1}", projectId, clientId);
}

これで明示的に渡したくない情報を裏でこっそり渡せます

クライアント情報を引数で渡す必要が無くなるので、サービスコントラクトがスッキリしそうです。
実用を考えると、IChannel をラップするクラスを作って、IContextManager を使って情報を設定する部分を隠ぺいした方が良いでしょうね。
サービス側でコンテキストを取り出すためのヘルパーを作るとなお良いです。