次の方法で共有



October 2009

Volume 24 Number 10

コントラクトファースト Web サービス - Windows Communication Foundation によるスキーマベースの開発

Christian Weyer | October 2009

「木を切り倒すのに 8 時間与えられたとしたら、斧を研ぐのに 6 時間使うだろう」とエイブラハム・リンカーンは言いました。これは、ソフトウェア エンジニアリングのさまざまな段階にも当てはまります。十分に考えられた設計を用意することが、ソフトウェアのほとんどのサクセス ストーリーの秘訣です。このことは、特に Web サービス (WS) などの通信システムを設計する場合に当てはまります。通信に使用する形式上のインターフェイスには、十分な注意を払う必要があります。このインターフェイスによって、システムの操作性と相互運用性が決まります。したがって、ライフサイクルの早い段階で、このインターフェイスを設計することに大きな意味があります。このインターフェイスは、コントラクトとも呼ばれます。今回のコラムでは、Web サービスを中心に、コントラクト ファースト手法を使用して Windows Communication Foundation (WCF) ベースのサービスを設計および開発する方法を説明します。WS コントラクトに関しては、大別して "コードファースト、コントラクトファースト開発" と "スキーマファースト、コントラクトファースト開発" の 2 つの方法論があります。ここでは、主に後者を取り上げます。

コントラクトファースト開発とは

コントラクトファーストの設計および開発は、新しいものではありません。これは正式には、Bertrand Meyer によって、彼の Eiffel プログラミング言語デザインの一部として紹介されたもので、1986 年以来、さまざまな技術文書に登場しています1、2。したがって、古いツールやテクノロジの観点からコントラクトを理解することは、その有用性を把握するうえで役に立つ可能性があります。

現在のコンピューターは、簡単な計算から地球の周囲を回る衛星の制御まで、多くの作業を行うことができますが、入出力マシンという基本的な考えは、19 世紀に加算用の計算機が発明されて以来、変わっていません。したがって、ソフトウェア エンジニアは、なんらかの入力を受け取り、なんらかの作業を実行して、なんらかの出力を行うという関数を現在でも作成しています。このような関数は作成後、他の場所から使用されます。関数のコントラクトでは、その関数の想定と実行結果を定義します。つまり、関数に渡される入力パラメーターは想定、戻り値は実行結果と考えることができます。関数のユーザーは、コントラクトさえ把握しておけば、関数を使用できます。この典型的な例が C++ のヘッダー ファイルです。別の C++ ライブラリの関数を呼び出す場合、実装を確認しようとは思わないでしょう。実際、ほとんどの場合は、実装にアクセスすることはできません。このプレーン テキストのヘッダー ファイルは、目的の関数を呼び出すために必要なものと、関数が完了した時点で得られるものについての十分な情報を、ユーザーとコンパイラーに提供します。

COM および ActiveX のタイプ ライブラリや、C# のインターフェイスも、一般にはまた別の形のコントラクトと見なされます。コントラクトは、ソフトウェア コンポーネントの抽象データ型を基に、形式上の正確かつ検証可能なインターフェイス仕様を定義する手段であるため、コントラクトを先に作成するのが自然な傾向です。これが、ほぼすべての C または C++ プログラマーが、まずヘッダー ファイルを作成することからプログラム開発を始める理由です。

WS のコントラクトも例外ではありません。WS 開発者としては、Web サービスとして入出力システムを共有することを考えます。このサービスは、プラットフォームの境界をまたぐ可能性があります。Web サービスのユーザーは実装の詳細を気にしないようにと、何度も聞かされていることと思います。コントラクトにアクセスできるだけで、だれでも Web サービスを利用できます。Web サービスはプラットフォームに依存しないため、Web サービス記述言語 (WSDL) や XML スキーマ (XSD) など、相互運用性の高い、標準ベースの構成要素を使用してコントラクトを定義します。コントラクトをモデル化する場合は、Web サービスのデータ、メッセージ、およびインターフェイスをモデル化します。

まず、クライアントとサービス間のデータの受け渡しに使用されるデータ コントラクト (つまり、データ構造) をモデル化します。データ コントラクトには、string、integer、double、complex などの単純なプリミティブ型を使用することも、Product、Employee、Itinerary などのドメイン固有の型を使用することもできます。

"RPC/Encoded" スタイルでは、スキーマに定義されている型を使用して、WSDL メッセージのパーツを定義します。このスタイルは一部しかスキーマの定義を表現しないため、スキーマを基に SOAP 本文全体を検証することが難しくなります。残りの部分は、WSDL から構成されます。また、SOAP 1.1 仕様によって規定されているセクション 5 のエンコード規則に従って、SOAP 本文の内容をエンコードします。このスタイルは有効なスタイルですが、WS-I に準拠していません。

"RPC/Literal" スタイルは、RPC/Encoded スタイルと非常によく似ています。ただし、RPC/Encoded スタイルと異なり、SOAP 本文をエンコードしません。このスタイルは WS-I に準拠していますが、やはり検証しにくいスタイルです。

"Document/Literal" では、要素を使用して WSDL メッセージのパーツを定義し、本文の内容をエンコードしません。これにより、上記のスタイルの最大の問題点が両方とも解消されます。このため、このスタイルは、WS-I 準拠のスタイルとして広く採用されています。

"Document/Literal/Wrapped" は、マイクロソフトが導入したスタイルです。これは Document/Literal スタイルと同じですが、要素の内容を、操作の名前によって作成された要素の内部にラップします。本文に操作名を含めると、HTTP 以外のエンドポイントからメッセージをディスパッチしやすくなります。しかし、より重要なことは、要素内に 1 つの子要素のみを設定するように規定している WS-I 標準に準拠しやすくなることです。

通常、Web サービスは、SOAP メッセージ3 を交換して、クライアントと通信します。このようにメッセージのコントラクトをモデル化することが、コントラクトファースト開発の 2 番目の作業になります。モデル化の方法は、次のうちどの SOAP メッセージング形式を使用するかによって決まります。

  • RPC/Encoded
  • RPC/Literal
  • Document/Literal
  • Document/Literal/Wrapped

Document/Encoded と呼ばれる別の形式もありますが、これを使用している実装を見つけることは実に困難です。このコラムでは、Document/Literal/Wrapped のみを取り上げます。これは最もよく使用されている形式であり、Web Services Interoperability Organization (WS-I) にも準拠しているためです。

メッセージ コントラクトの定義には、2 つの側面があります。まず、SOAP 本文の構造を定義する必要があります。これには XSD を使用します。また、前の手順で定義したデータ コントラクトを使用することもできます。メッセージ コントラクトのもう 1 つの側面は、SOAP ヘッダーの構造の定義です。メッセージのヘッダーは、WSDL で定義されます。一般的に、このヘッダー内容の定義には、最初の作業でモデル化したデータ コントラクトを使用します。

データ コントラクトとメッセージ コントラクトを用意したら、サービスから提供する操作を 1 つ以上定義して、インターフェイスをモデル化できます。操作コントラクトでは、2 番目の作業でモデル化したメッセージ コントラクトを使用して、操作中に交換されるメッセージを定義できます。

データ、メッセージ、およびインターフェイスの 3 種類の主要コントラクトに加えて、Web サービス コントラクトには、ポリシー、バインド、およびエンドポイントも含まれます。図 1 に、Web サービス コントラクトの各種成果物の表現に、WSDL とスキーマのどちらの構成要素が使用されるかをまとめています。


図 1 WSDL 構成要素と XSD 構成要素の用途

コードファーストとスキーマファーストの比較

はじめに述べたように、コントラクトをモデル化する方法には、"コードファースト" と "スキーマファースト" の 2 とおりの方法があります。ニーズにあった方法を選ぶには、両方を理解することが重要です。

コードファーストの方法では、複数の Web サービス スタック (WCF、ASMX、JAX-WS など) が提供する強力な宣言型のコントラクト プログラミング構成要素を使用できます。このため、お気に入りのエディターでお気に入りのプログラミング言語を使用して、コントラクトをモデル化できます。したがって、WSDL や XML スキーマ構成要素を学習しなくても、既によく理解しているプログラミング構成要素やデータ型を使用できます。ネイティブ形式 (WSDL や XSD) での WS コントラクトの生成という面倒な作業は、基盤の WS スタックによって処理されます。このような宣言型のプログラミング構成要素を使用すると、新しい Web サービスをゼロから構築することも、既存の実装をサービスとして公開することも、はるかに簡単になります。

ただし、このように簡単で便利であることから、モデル化する成果物表現の基になる WSDL や XSD 構成要素を意識しないで使用すると、ちょっとした問題が生じることがあります。そう、これは先ほど WSDL と XSD の知識なしにコントラクトを開発できると述べたことと矛盾します。しかし、残念ながら、コードファーストの抽象化には欠点があります。その理由を .NET 開発者の視点から考えて見ましょう。

コーディング作業をするときは、.NET の型システムを使用したモデリング オブジェクト グラフの考え方から抜け出していません。たとえば、コードファーストの方法では、"System.Data.DataSet" や "System.DateTimeOffset" などの .NET 固有の型の使用は制限されません。これらの型は、間違いなく XML で表現できます (また、XML シリアル化も可能です)。しかし、.NET 以外のクライアントは、これらの型にアクセスできません。

もう 1 つのよい例は、循環グラフです。前述のとおり、コードファーストの方法では、オブジェクト指向の考え方から抜け出していません。したがって、オブジェクト グラフをモデル化する傾向にあり、このようなグラフにはおそらく循環参照があります。循環参照がある次のデータ構造について考えて見ましょう。

public class OrderItem
    {
        public Order Order { get; set; }
    }

    public class Order
    {
        public List<OrderItem> Items { get; set; }
    }

Order には OrderItems の一覧があり、各 OrderItem は親の Order を逆参照しています。これを XML で表現しようとすると、XML にはオブジェクト ID という概念がないことが問題になり、永遠に処理が終わらない 図 2 のような XML になる可能性があります。

図 2 循環参照があるオブジェクト グラフの XML 表現

<Order>
  <Items>
    <OrderItem>
      <Order>
        <Items>
          <OrderItem>
            ...
          </OrderItem>
        </Items>
      </Order>
    </OrderItem>
  </Items>
</Order>

この動作は、XML ドキュメントの階層的性質と、オブジェクト グラフのグラフ モデルの違いに起因します。残念ながら、これはコードファーストの方法に従っているときは、容易には検出できません。これを解決するには、DataContractAttribute の IsReference プロパティを使用して、DataContractSerializer で参照の追跡を有効にする必要があります。しかし、そこで、また別の問題に行き当たります。まず、標準のスキーマ検証 API を使用して、任意のスキーマを基に XML ドキュメントを検証できません。また、シリアライザーが使用する標準の参照追跡メカニズムは、SOAP 1.1 の仕様で規定されているセクション 5 のエンコード規則に基づいています。これらの規則は、WS-I 仕様の指定の標準である、document/literal スタイルのメッセージでは廃止されています。

コードファーストの方法にはこのような問題がありますが、それでも WCF プログラミング構成要素を使用する方が、WSDL および XSD 構成要素の対応関係と、WCF のシリアル化のしくみについての知識がわずかしかない場合は、WSDL および XSD 構成要素を使用するよりも簡単に思えるかもしれません。コードファースト、コントラクトファーストの方法の詳細についてはこのコラムでは説明しませんが、次の図は、最もよく使用される WCF コントラクト プログラミング構成要素と WSDL または XSD 構成要素間の関係の概要を示しています。

図 3 よく使用される WCF の宣言型プログラミング構成要素と WSDL またはスキーマ構成要素間の対応関係

コードファーストの設計に代わる一番の方法は、スキーマファーストの設計です。この設計では、直接 WSDL および XSD をモデル化に使用します。これは、ネイティブの構成要素を扱うことになるため、より自然に WS コントラクトをモデル化できます。WSDL と XSD を使用すると、より XML を中心とした考え方でコントラクトをモデル化でき、コードファーストの方法が持つ多くの欠点を解消できます。

ただし、この方法で作業を進める場合は、WSDL と XSD についてかなり詳しい知識が必要です。また、WSDL と XSD の両方が関係するため、WSDL と XSD のどちらか一方に考えを集中することはできません。たとえば、スキーマファーストの方法では、データ構造をプログラムから操作できるように、ある時点でスキーマからコードを生成する必要があります。現在利用可能な複数の WS スタックが、このためのツールを提供しています (WCF で使用できるツールについては、このコラムで後述します)。ただし、XML スキーマの仕様は膨大であるため、これらのツールは、多くの場合、スキーマ構成要素の一部しかサポートしません。したがって、スキーマでサポートされていない構成要素を使用する場合は、やはり相互運用性の面で問題になります。また、XSD 制約のようなある特定のモデリング対象については、宣言型の構成要素を用意していないツールキットが多いため、コード生成時に失われる可能性があります。

コードファーストの方法では、コードを作成して、ツールキットから自動で WSDL および XSD を生成します。したがって、コードを変更したら、生成済みのスキーマに自動的に変更が反映されます。スキーマファーストの方法では、適切な変更プロセスがなく、規律正しい開発者がチームにいないと、WSDL と XSD が古くなる可能性があります。また、IDE には WSDL のモデリング機能が標準で付属していない場合があるため、WSDL のモデリング ツールが必要になる可能性もあります。

概して、コードファーストの方法にも、スキーマファーストの方法にも、長所と短所があります。スキーマファーストの方法は、たとえば関係者の同意を得るために、開発ライフサイクルの非常に早い段階でスキーマをモデル化していて、既存のスキーマを使用する必要があるシナリオで特に有用です。これは、行政や銀行においてよくある状況です。また、Open Travel Alliance (OTA) などの既存の業界標準に準拠するアプリケーションを作成する場合も、既存のスキーマを使用する必要があります。コントラクトの定義に 2 つの方法のどちらを選ぶかは、実際のシナリオ、リソース、およびチームの技量も踏まえて考える必要があります。たとえば、他のプラットフォームで実行するクライアントをサポートする必要がない WCF サービスを作成する場合は、おそらくスキーマファーストの方法を検討する必要はないでしょう。また、WCF のコントラクト プログラミング構成要素にかなり慣れていて、シリアル化プロセスについての詳しい知識があり、オブジェクト グラフではなく階層型構造に集中できる場合は、コードファーストの方法に従って、同じ結果を達成できます。

では、この時点でスキーマファーストの方法を採用することに決められた方は、これ以降のセクションを参照して、スキーマファーストを使用して WCF サービスを開発する方法を確認してください。

スキーマファーストの方法について

スキーマファーストの方法には、5 種類の作業があります (図 4 参照)。


図 4 スキーマファースト、コントラクトファーストの開発プロセス

最初の 3 つの作業については、コラムの冒頭で既に説明しました。したがって、これらについては簡単な説明だけを行い、その先のいくつかの重要な点に進みましょう。

作業 1 データのモデル化: XML スキーマを使用して、サービスとクライアント間のデータ転送に使用するデータ構造をモデル化します。Visual Studio の XSD エディターを使用することも、Altova XmlSpy や Liquid XML Studio などの同様のツールを使用することもできます。

作業 2 メッセージのモデル化: 前述のとおり、この作業では XML スキーマを使用してメッセージ本文の構造をモデル化します。おそらく、ここでは作業 1 でモデル化したデータ構造を使用します。

作業 3 インターフェイスのモデル化: この作業では、インターフェイスの操作を定義することで、インターフェイスのコントラクトをモデル化します。ただし、ここでは前の作業とは異なり、XML スキーマではなく WSDL を使用する必要があります。WSDL の編集機能は Visual Studio にはありませんが、Altova XmlSpy などの特定の製品で提供されています。

作業 4 コードの生成: データ、メッセージ、および操作のモデル化が済んだら、お気に入りのプログラミング言語でこれらの成果物を表現するコードを生成します。次の 2 つのセクションでは、現在 WCF 開発者が利用できるツールを使用して、コードを生成する方法について詳しく説明します。

作業 5 コントラクトの設計とコード生成の繰り返し: 1 回で完璧な結果を得ることは非常に困難です。コントラクトを設計したら、必要とする最適な構造が見つかるまで、何度かリファクタリングすることになるでしょう。

スキーマファースト、コントラクトファースト開発での標準 WCF ツールの使用

このシナリオを説明するために、ホスト コンピューターでアクティブなプロセスの一覧を取得する操作を実装した簡単な Web サービスを作成しましょう。データ コントラクトを皮切りに、2 つの複合型をモデル化します。

  1. "process" 複合型は、ホスト コンピューターのプロセスを表します。
  2. "processList" 複合型は、"process" 型によって定義されたプロセスの一覧を表します。

次に、入出力メッセージの本文を抽象化するために、"explore" と "exploreResponse" という 2 つの要素をモデル化します。前述のとおり、WS-I に準拠するように、メッセージ本文で "soap:body" 要素内に子要素が 1 つだけあることを確認します。WCF でも ASMX でも、操作名を使用してラッパー要素を作成することで、これを実現します。メッセージのモデル化に使用する名前付け規則が WCF 標準と異なる場合、コード生成ツールは要求と応答用に "MessageContract" クラスを生成します。ここでは、このプロセスで後ほどコードを生成するときに、より単純なオブジェクト モデルが得られるように、WCF 名前付け規則を使用して、メッセージ要素の名前を付けています。

最後に、外部ツールを使用して WSDL をモデル化します。ここでは、この作業に Altova XML Spy を使用しました。

図 45、および 6 には、それぞれ、前述のデータ、メッセージ、およびインターフェイスのコントラクト定義が含まれています。

図 4 Data.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
           xmlns:ns1="http://schemas.thinktecture.com/contractfirst/2009/07/data" 
           targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/data" 
           elementFormDefault="qualified" 
           attributeFormDefault="unqualified"
           >
    <xs:complexType name="process">
        <xs:sequence>
            <xs:element name="pid" type="xs:int"/>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="processList">
        <xs:sequence>
            <xs:element name="process" type="ns1:process" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

図 5 Messages.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:data="http://schemas.thinktecture.com/contractfirst/2009/07/data"
           xmlns:ns1="http://schemas.thinktecture.com/contractfirst/2009/07/"
           targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/"
           elementFormDefault="qualified">
  <xs:import namespace="http://schemas.thinktecture.com/contractfirst/2009/07/data"
             schemaLocation="data.xsd"/>
  <xs:element name="explore">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="hostname" type="xs:string" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="exploreResponse">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="processes" type="data:processList" nillable="true"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>  
</xs:schema>

図 6 ProcessExplorerService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/" 
                  xmlns:soap="https://schemas.xmlsoap.org/wsdl/soap/" 
                  xmlns:http="https://schemas.xmlsoap.org/wsdl/http/" 
                  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                  xmlns:soapenc="https://schemas.xmlsoap.org/soap/encoding/" 
                  xmlns:mime="https://schemas.xmlsoap.org/wsdl/mime/" 
                  xmlns:tns="http://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:soap12="https://schemas.xmlsoap.org/wsdl/soap12/" 
                  xmlns:msg="http://schemas.thinktecture.com/contractfirst/2009/07/" 
                  xmlns:ns="http://schemas.thinktecture.com/contractfirst/2009/07/data"
                  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
                  targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/">
    <wsdl:types>
        <xs:schema targetNamespace="http://schemas.thinktecture.com/contractfirst/2009/07/" 
               elementFormDefault="qualified">
            <xs:import schemaLocation="messages.xsd"/>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="exploreRequestMessage">
        <wsdl:part name="parameters" element="msg:explore"/>    
    </wsdl:message>
    <wsdl:message name="exploreResponseMessage">
        <wsdl:part name="parameters" element="msg:exploreResponse"/>
    </wsdl:message>
    <wsdl:portType name="processExplorer">
        <wsdl:operation name="explore">      
            <wsdl:input wsaw:Action="http://schemas.thinktecture.com/contractfirst/2009/07/explore" 
                  message="tns:exploreRequestMessage"/>
            <wsdl:output wsaw:Action="http://schemas.thinktecture.com/contractfirst/2009/07/exploreResponse" 
                   message="tns:exploreResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="processExplorerHttpBinding" type="tns:processExplorer">
        <soap12:binding style="document" transport="https://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="explore">
            <soap12:operation 
        soapAction="http://schemas.thinktecture.com/contractfirst/2009/07/explore" 
        soapActionRequired="true"/>
            <wsdl:input>        
        <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="ProcessExplorerService">
        <wsdl:port name="processExplorerPort" binding="tns:processExplorerHttpBinding">
            <soap12:address location="http://localhost/processexplorer"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

コントラクトが準備できたら、Visual Studio 2008 コマンド プロンプトを開き、svcutil.exe を使用して、次のコードのような WCF の C# コードを生成できます。

svcutil -noconfig -serializer:datacontractserializer -d:../ -
namespace:*,Thinktecture.Samples.ProcessExplorerService
ProcessExplorerService.wsdl Messages.xsd Data.xsd

Svcutil.exe は、基本的にクライアント側コードの生成に使用されます。この前身の wsdl.exe と異なり、Svcutil.exe にはサービス側のスケルトンを生成するオプションはありません。したがって、生成されたコードには、サービス側の実装には必要のない、クライアント側のプロキシ コードが含まれます。サービスを実装する際は、生成されたコードから、これら無関係の型を削除する必要があります。このコラムのサンプルでは、このため、生成済みのコードから "processExplorerClient" クラスと "processExplorerChannel" クラスを削除しています。削除できたら、付属のサンプル ソリューションの "ProcessExplorerService1" プロジェクトを参考に、生成されたインターフェイスの型 "processExplorer" を実装します。

最後に、サービスをホストする前に、WSDL の自動生成を無効にし、WCF のメタデータ生成ランタイムが静的な WSDL ファイルを指すように、"ServiceMetadataBehavior" を調整する必要があります。次のコードでは、WCF 構成に外部メタデータの場所を指定しています。

<serviceMetadata httpGetEnabled="true"                            
externalMetadataLocation="..\contracts\processexplorerservice.wsdl"/>

お気付きかもしれませんが、この方法を使用してスキーマファースト開発を行うことは、実際のアプリケーションでは少し面倒になる可能性があります。無料の Web service Contract First (WSCF) ツールの後継である WSCF.blue は、この問題に対応するために作成されています。

WSCF.blue の概要

WSCF.blue は、無料の Visual Studio 2008 アドインです。その最大の機能は、お気に入りの IDE でスキーマファースト、コントラクトファースト開発ができるようにすることです。Visual Studio の XML スキーマ エディター、または Liquid XML Editor のような無料ツールを使用して、データ コントラクトとメッセージ コントラクトをモデル化します。Visual studio 2008 のスキーマ エディターは Visual Studio 2005 に付属していたものよりも、モデル化のサポートが縮小されているため、後者を使用する方が便利な場合もあります。スキーマの準備ができたら、WSCF.blue の WSDL 生成ウィザードを使用して、サービス インターフェイスをモデル化します。これは、インターフェイスの操作をモデル化し、実際の WSDL 構築の詳細を隠す、便利な機能です。このウィザードは、メッセージ コントラクトを含む XSD ファイルを右クリックして、コンテキスト メニューの [Create WSDL Interface Description] (WSDL インターフェイスの記述の作成) をクリックして、起動できます (図 7 参照)。


図 7 WSDL 生成ウィザードの起動

このウィザードは、サービス インターフェイスに実装する操作のコントラクトについての情報を収集する複数の手順から構成されます。ほとんどは、かなりわかりやすい手順です。ただし、手順 2、3、および 4. では、注意が必要なことがいくつかあります。

ウィザードを起動するときにソリューション エクスプローラーで選択したスキーマ ファイルに定義されている型は、WSDL モデリングに使用できます。ただし、他の XSD ファイルにその他の型がある場合は、手順 2. でそれらについてもインポートできます (図 8 参照)。


図 8 追加のスキーマ ドキュメントのウィザードへのインポート

手順 3. では、操作をモデル化します。ウィザードには、メッセージ コントラクトの要素をスキャンし、操作を推論できる機能もあります。ただし、この機能は、ウィザードの推論規則で使用されている名前付け規則を使用するメッセージにしか使用できません (図 9 参照)。


図 9 インターフェイスへの操作の追加または削除

手順 4. で、モデル化したメッセージを、操作の要求および応答にマップできます。手順 3. の推論機能を使用する場合、このマッピングはウィザードによって自動的に行われます。また、この手順では、要求メッセージと応答メッセージのメッセージ ヘッダーを定義することもできます (図 10 参照)。


図 10 操作の入力メッセージと出力メッセージの定義

最終的に、ウィザードが完了したら、生成された WSDL ファイルがプロジェクトに追加されます。
コントラクトをモデル化できたので、コード生成ツールの WSCF.blue を使用して、プロジェクトの言語で WCF コードを生成できます。ソリューション エクスプローラーで WSDL ファイルを右クリックし、コンテキスト メニューの [Generate Web Service Code] (Web サービスのコードの生成) をクリックして、コード生成ツールを起動します (図 11 参照)。


図 11 コード生成ツールの起動

コード生成のダイアログ ボックスでは、さまざまなコード生成オプションを利用できます。svcutil.exe で提供されているオプションのほとんどと、その他に追加のオプションがいくつかあります。たとえば、サーバー側コードを生成するためのサーバー側スタブ、シリアル化に影響することなくスキーマで使用されているキャメル形式の名前を Pascal 形式名に変換するための大文字と小文字の調整、対応するスキーマ成果物を表現するために生成された CLR 型ごとに専用のコード ファイルを生成するファイルの分離などの機能があります (図 12 参照)。図 13 に、コード生成ツールで利用できるすべてのオプションをまとめました。


図 12 コード生成オプション

図 13 WSCF.blue のコード生成オプション

オプション 説明
Client-side proxy (クライアント側プロキシ) サービスのユーザー用に、クライアント側のプロキシを生成します。
Service-side stub (サービス側スタブ) サービスの実装者用に、サービス側のコードを生成します。
Public properties (パブリック プロパティ) 生成されたクラス メンバーをパブリック プロパティとして公開します。
Serializable classes (シリアル化可能クラス) 生成されるクラスに SerializableAttribute が設定されます。
Collections (コレクション) Collection<T> 型を使用して、コレクション型を表します。
List<T> (List<T>) List<T> 型を使用して、コレクション型を表します。
Data binding (データ バインド) 生成されるクラスをデータ バインド可能なコンポーネントにバインドするために必要なコードを生成します。
Order identifiers (順序 ID) 使用される DataMemberAttribute ごとに Order プロパティを生成します。
Async methods (非同期メソッド) 操作の非同期バージョンを生成し、OperationContextAttribute.AsyncPattern プロパティを true に設定して、非同期メソッドがあることを WCF に通知します。
Separate files (ファイルの分離) 生成された各クラスを専用のファイルに配置します。
Adjust casing (大文字と小文字の調整) シリアル化プロセスに影響することなく、生成されたクラスとそのプロパティに名前を付けるときは、Pascal 形式を使用します。
Concurrency mode (同時実行モード) ServiceBehaviorAttribute.ConcurrencyMode プロパティ用に生成される値を指定します。
Instance context mode (インスタンス コンテキスト モード) ServiceBehaviorAttribute.InstanceContextMode プロパティ用に生成される値を指定します。
Use synchronization context] (同期コンテキストの使用) ServiceBehaviorAttribute.UseSyncrhronizationContext プロパティ用に生成される値を指定します。
Enable WSDL endpoint (WSDL エンドポイントの有効化) サービスにエンドポイントを追加して、静的メタデータ ファイルを公開します。これは、自己ホスト型サービスで静的メタデータ ドキュメントを公開する場合は必須です。
Destination file name (出力先ファイル名) 出力ファイルの名前です。[Separate files] (ファイルの分離) オプションが有効な場合、このパラメーターは無視され、クラス名がファイル名に使用されます。
Destination namespace (出力先名前空間) 生成される型に使用される CLR 名前空間。
Remember settings (設定を保存) 最後に使用されたコード生成オプションを保存します。
Overwrite existing files (既存のファイルを上書き) 一意のファイル名を作成して競合を解消するのではなく、WSCF.blue による既存のファイルの上書きを許可することを指定します。生成済みコードに対するカスタマイズは、すべて上書きされることに注意してください。したがって、そのような変更は部分クラスを使用して行うことをお勧めします。

このコラムで説明している WSCF.blue のリリース (2009 年 7 月 25 日現在は Beta 1) では、構成ファイルのマージがサポートされていません。したがって、サービスをホストするために必要な構成は、output.config ファイルに出力されます。必要に応じて、手動でこのファイルの内容を web.config または app.config に移動する必要があります。

構成ファイルが準備できたら、引き続き、生成されたサービス クラスに必要な実装を追加できます。

また、Visual Studio でスキーマを変更し、WSDL 生成ウィザードの WSDL ラウンドトリップ機能を使用してコントラクトを変更し、コードを生成し直すことができます。

次のステップ

WSCF.blue はオープン ソース プロジェクトとしてリリースされており、wscfblue.codeplex.com からダウンロードできます。現在のベータ リリースにはいくつか制限があり、基本的ですが最もよく使用される Web サービス実装しか生成できない可能性があります。たとえば、WSDL 生成ウィザードは、現時点では基本の HTTP バインディングしかサポートしていません。また、現在の WSCF.blue によるコード生成では、より広範なスキーマ コントラクトをサポートする "DataContractSerializable" クラスではなく、"XmlSerializable" クラスしか生成されません。WSCF.blue のすべての既知の問題の一覧とロードマップについては、CodePlex サイトを参照してください。

WSCF の使用方法の詳細なチュートリアルが、thinktecture.com/resourcearchive/tools-and-software/wscf/wscf-walkthrough で公開されています。ドキュメントは WSCF 0.6 を基にしていますが、説明されている手順は、同じように使用できます。

まとめ

Web サービスのスキーマベースのコントラクトファースト モデリングでは、XML を中心とする考え方で、コントラクトをモデル化できます。このプロセスでは、汎用性の高い型と、XML で表現できる階層型データ構造を基本に取り組むことができます。Web サービス プラットフォームの成功には、ツールのサポートが欠かせません。WSCF.blue は、WCF に標準で付属しているツールを拡張して、WCF 開発者がより簡単にスキーマベースのコントラクトファースト開発に取り組めるようにしています。

Christian Weyer は thinktecture の共同設立者かつ主設計者であり、Java、COM、DCOM、COM+、Web サービス、WCF、およびその他のテクノロジを使用する分散アプリケーションのモデル化と実装を行ってきました。定番の .NET 用ツールである Web Services Contract First の開発者です。連絡先は thinktecture.com/staff/christian です。

Buddhike de Silva は、オーストラリアを拠点に活躍しているエンジニアです。オーストラリアに移住する前は、thinktecture で主任エンジニアを務めていました。長年にわたり分散アプリケーションの設計と実装に携わっています。また、WSCF/WSCF.blue プロジェクトにも長年にわたって貢献しています。連絡先は http://blogs.thinktecture.com/buddhike です。

1「Design by Contract (コントラクトによる設計)」、Technical Report TR-EI-12/CO、Interactive Software Engineering Inc.、1986 年

2http://se.ethz.ch/~meyer/publications/computer/contract.pdf

3RESTful サービス。ただし、標準の HTTP メッセージを交換してクライアントと通信し、完全に異なるパラダイムに従います。