ものがたり(旧)

atsushieno.hatenablog.com に続く

WCF AnnouncementServiceに見られる、複数バージョンの同一仕様のサポート

しばらく何も書いていなかったので、ひさしぶりにWCFの解剖みたいなネタを書いてみようと思う。PCが故障していた間に現実逃避でWCF4の新機能のひとつWCF Discoveryを実装していて、終わった後も中途半端に放置して戻るのもなんなのでちまちまと続けているので、その中から。

AnnouncementServiceは少々謎のクラスだ。これはServiceHost上でsingleton instanceとして使われるために設計されたクラスで、当然ながらservice contractを実装した型でなければならないのだけど、型定義を見る限り、どこにもservice contractとなるインターフェースを実装している様子はない。そのくせServiceBehaviorAttributeが付いているので、これがServiceTypeであることは間違いない。

ServiceHost上でホストされた場合に、AnnouncementServiceを使用して公開できるSOAPサービス*1は、実はひとつではない。というのは、WCF Discoveryでは、新旧あわせて3つの仕様を全部サポートしているからだ:

  • OASISのWS-Discovery 1.1 最終版 (Version11)
  • OASISのWS-Discovery 1.1 committee draft (VersionCD1)
  • schemas.xmlsoap.orgにあるMS独自仕様のWS-Discovery (VersionApril2005)

面倒なことに、それぞれが別々のXML namespaceを使用しているので、Service Contractがバラバラになっている。にもかかわらず、HelloやByeといったメッセージの内容はどれも(たぶん)変わらない。

いずれにしろ、たとえ実質的に内容がかぶっていても、これら別々のcontractは、並列して公開するなら、別々のServiceEndpointで公開することになる。ちなみにWCF 4.0にはstandard endpointsという概念が新たに追加されていて、ここではそのひとつであるAnnouncementEndpointというものが使われることになる*2。これについてはすぐ後で説明する。

そして、これら3つの仕様に対応するservice contractは、実はinternalなinterfaceとしてAnnouncementServiceで実装されているようだ。typeof (AnnouncementService).GetInterface() を呼びだすと、これらの仕様に対応すると思われるinterface型が返される。

次にAnnouncementEndpointについて。これはServiceEndpointから派生したクラスで、これを含む、いわゆるstandard endpointsは、皆ServiceEndpointから派生した独自のクラスとなっているようだ。ServiceEndpointには、Contract, Binding, Addressの3つを指定しなければならないはずだが、AnnouncementEndpointの場合、コンストラクタに渡されるDiscoveryVersionをもとに、contractが上記のいずれかが自動的に設定されるようになっている(そのcontract typeは上記の通り公開されていないわけだが…)。各DiscoveryVersionに対応するservice contractは、このAnnouncementEndpointを使って確認することができる。

AnnouncementServiceおよびAnnouncementClientでは、AnnounceOnline/OnlineAnnouncementおよびAnnounceOffline/OfflineAnnouncementのためのメソッドが定義されているけど、実際にはこれらはHelloとByeということになろう(未確認)。これらのメッセージの処理は、AnnouncementServiceにおいては、OnBeginOnlineAnnouncement()とかOnBeginOfflineAnnouncement()といったメソッドで実装されて、それぞれに対応したイベントが呼び出されることになるわけだけど、これらはservice contractのメソッドというわけではない(どのservice contractのメソッドを実装しているわけでもない)。

ではWCFがHello/Byeメッセージを受信してから、どのようにこれらのメソッドを呼び出すようになっているのかというと、AnnouncementServiceが実装している「はずの」各service contractのメソッドが、インターフェースの明示的実装として存在していて*3、それらが内部的にOnBeginほげほげ()を呼び出していると考えられる*4

そして、各バージョンのservice contractで使用されているであろうメッセージのシリアライゼーションに使用されているはずのクラスが、System.ServiceModel.Discovery.VersionXXX という3つのCLR namespaceで公開されている。実際には、DiscoveryMessageSequenceに対応するAppSequenceはメッセージヘッダ項目なので、ヘッダ内容をカスタマイズするために存在するMessageContract(Attribute)が使用されているはずだ。

そんなわけで、AnnouncementServiceの実体は多分こんな感じになっていると思う。まあ、まだ実装は終わっていないのだけど。

というわけで、今回はAnnouncementServiceを槍玉にあげて、無意味にXML namespaceが変わって実体がほぼ変わらない仕様をサポートするアプローチのひとつを謎解きしてみた。気が向いたらクライアント側なども書くかもしれない(多分気は向かない)。

*1:残念ながらWS-DiscoveryはSOAPが前提だ

*2:というのが一般的だろう。必須というわけではない。

*3:publicなメソッドにそれらが見当たらない以上、明示的な実装になっていると考えるしかない

*4:そう考えれば、Beginほげほげ()が無いのにOnBeginほげほげ()があることにも得心が行く