開発者の視点から見た ASP.NET Web サービスと WCF との比較

Windows Communication Foundation (WCF) には ASP.NET 互換モードがあります。このモードでは、WCF アプリケーションを ASP.NET Web サービスと同じようにプログラミングおよび構成し、その動作を真似ることができます。 以下のセクションでは、ASP.NET Web サービスと WCF を、それぞれの技術を使ってアプリケーションを開発する視点から比較してみます。

データ表現

ASP.NET で Web サービスを開発する場合、通常はまず、このサービスが使う複合データ型の定義から始めます。 ASP.NET は XmlSerializer を利用して、.NET Framework 型で表されたデータを XML 形式に変換してサービスとの間でやり取りしたり、XML 形式で受け取ったデータを .NET Framework オブジェクトに変換したりします。 ASP.NET サービスで使用する複合データ型を定義するには、XmlSerializer で XML 形式にシリアル化したり、XML 形式から逆シリアル化したりできる .NET Framework クラスの定義が必要です。 クラス定義は手で記述するほか、XML スキーマの型定義から生成することも可能です。それにはコマンド ライン上で実行する XML スキーマ/データ型サポート ユーティリティである xsd.exe を使います。

XmlSerializer で XML 形式にシリアル化したり、XML 形式から逆シリアル化したりできる .NET Framework クラスを定義する場合に知っておくべき主な事項を次に示します。

  • XML に変換されるのは、.NET Framework オブジェクトのパブリック フィールドおよびパブリック プロパティに限ります。

  • コレクション クラスのインスタンスを XML 形式にシリアル化できるのは、そのクラスが IEnumerable または ICollection インターフェイスを実装している場合に限ります。

  • IDictionary インターフェイスを実装した、Hashtable などのクラスを、XML 形式にシリアル化することはできません。

  • System.Xml.Serialization 名前空間の属性型は、大部分が .NET Framework クラスやそのメンバーに追加可能であり、これにより、XML での当該クラスのインスタンスの表現方法を制御できます。

通常は、WCF アプリケーションを開発する場合も、初めに複合型を定義します。 WCF でも ASP.NET Web サービスと同じ .NET Framework 型を使用できます。

WCFDataContractAttributeDataMemberAttribute を .NET Framework 型に追加して、ある型のインスタンスを XML にシリアル化できる旨や、当該型のどのフィールドやプロパティを実際にシリアル化できるのかを指定することができます。その例を以下に示します。

//Example One:
[DataContract]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

//Example Two:
public class LineItem
{
    [DataMember]
    private string itemNumber;
    [DataMember]
    private decimal quantity;
    [DataMember]
    private decimal unitPrice;

    public string ItemNumber
    {
      get
      {
          return this.itemNumber;
      }

      set
      {
          this.itemNumber = value;
      }
    }

    public decimal Quantity
    {
        get
        {
            return this.quantity;
        }

        set
        {
            this.quantity = value;
        }
    }

    public decimal UnitPrice
    {
      get
      {
          return this.unitPrice;
      }

      set
      {
          this.unitPrice = value;
      }
    }
}

//Example Three:
public class LineItem
{
     private string itemNumber;
     private decimal quantity;
     private decimal unitPrice;

     [DataMember]
     public string ItemNumber
     {
       get
       {
          return this.itemNumber;
       }

       set
       {
           this.itemNumber = value;
       }
     }

     [DataMember]
     public decimal Quantity
     {
          get
          {
              return this.quantity;
          }

          set
          {
             this.quantity = value;
          }
     }

     [DataMember]
     public decimal UnitPrice
     {
          get
          {
              return this.unitPrice;
          }

          set
          {
              this.unitPrice = value;
          }
     }
}

DataContractAttribute は、当該型の中にシリアル化可能なフィールドやプロパティがあることを示し、具体的にどのフィールドやプロパティをシリアル化できるかを DataMemberAttribute で示します。 DataContractAttribute はクラスにも構造体にも適用できます。 DataMemberAttribute はフィールドやプロパティに適用します。これはパブリックでもプライベートでもかまいません。 DataContractAttribute が適用された型のインスタンスのことを、WCF ではデータ コントラクトと呼びます。 これを XML 形式にシリアル化するには DataContractSerializer を使います。

DataContractSerializer を使う場合と、XmlSerializer および System.Xml.Serialization 名前空間に定義された属性を使う場合の、主な違いを以下に示します。

  • XmlSerializerSystem.Xml.Serialization 名前空間の属性は、XML スキーマで定義された有効な型であればどれにでも .NET Framework 型をマップできるようにするためのものです。これらを使うと、XML での型の表現方法を細かく制御できます。 これに対し、DataContractSerializerDataContractAttribute、および DataMemberAttribute の場合、XML での型の表現方法にはほとんど自由度がありません。 指定できるのは、名前空間と、型やフィールド、プロパティを XML で表す名前、フィールドやプロパティの XML における並び順だけです。

    [DataContract(
    Namespace="urn:Contoso:2006:January:29",
    Name="LineItem")]
    public class LineItem
    {
          [DataMember(Name="ItemNumber",IsRequired=true,Order=0)]
          public string itemNumber;
          [DataMember(Name="Quantity",IsRequired=false,Order = 1)]
          public decimal quantity;
          [DataMember(Name="Price",IsRequired=false,Order = 2)]
          public decimal unitPrice;
    }
    

    上記以外の事項は、DataContractSerializer に固定で組み込まれています。

  • DataContractSerializer の場合、型の XML での表現方法に自由度が小さく、したがってシリアル化のプロセスがあらかじめ大部分予測できるので、最適化が容易です。 DataContractSerializer を使えば、性能が約 10% 向上するという現実的な利点があります。

  • XmlSerializer を使用する方法では、どのフィールドやプロパティを XML にシリアル化するかを属性で指定することはできませんが、DataMemberAttribute の場合は DataContractSerializer 属性で明示的に指定できます。 このように、データ コントラクトとは、アプリケーションとの間でやり取りするデータ構造を明示した契約であると言うことができます。

  • XmlSerializer で XML 形式に変換できるのは、.NET オブジェクトのパブリック メンバーに限ります。一方 DataContractSerializer は、アクセス修飾子にかかわらず、どのメンバーでも変換可能です。

  • DataContractSerializer の場合、パブリックでないメンバーも XML に変換できるため、シリアル化できる .NET 型についての制約が少なくなります。 特に、Hashtable など、IDictionary インターフェイスを実装した型が変換可能です。 DataContractSerializer はさらに、既存の .NET 型のインスタンスを XML にシリアル化する場合でも、型定義を変更したり、ラッパーを定義したりする必要がありません。

  • DataContractSerializer はパブリックでないメンバーも変換できるため、完全に信頼できるコードからしか実行できないようになっています。XmlSerializer にはそのような制約がありません。 コードの完全信頼アクセス権限とは、当該マシン上のすべてのリソースにアクセスできる権限で、その確認にはコードを実行するための資格情報を使います。 マシン上のすべてのリソースに制限なくアクセスできるので、このようなコードの取り扱いには十分に注意しなければなりません。

  • DataContractSerializer にはバージョン管理の機能がいくつか組み込まれています。

    • DataMemberAttribute には IsRequired プロパティがあります。旧バージョンにはなかったメンバーを追加した場合に、そのプロパティを false とすれば、当該データ コントラクトの新バージョンを扱うアプリケーションが、旧バージョンのデータも扱えるようになります。

    • データ コントラクトに IExtensibleDataObject インターフェイスを実装すると、DataContractSerializer は、新バージョンのデータ コントラクトで定義されたメンバーを、旧バージョンのコントラクトを扱うアプリケーション経由で受け渡しできるようになります。

以上のようにさまざまな違いがありますが、XmlSerializer で既定の設定を使用して型をシリアル化したものは、XML の名前空間を明示的に定義してあれば、DataContractSerializer で型をシリアル化したものと意味的に同等です。 次のクラスには両方のシリアライザーと共に使用する属性があり、XmlSerializerDataContractAttribute では意味的に同等の XML に変換されます。

[Serializable]
[XmlRoot(Namespace="urn:Contoso:2006:January:29")]
[DataContract(Namespace="urn:Contoso:2006:January:29")]
public class LineItem
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}

Windows ソフトウェア開発キット (SDK) には、ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe) と呼ばれるコマンドライン ツールが含まれています。 ASP.NET ウェブ サービスで使用される xsd.exe ツールと同様に、Svcutil.exe では XML スキーマから .NET データ型の定義を生成できます。 DataContractSerializer が XML スキーマで定義された形式の XML を出力できる場合、型はデータ コントラクトの形に変換されます。そうでなければ、XmlSerializer を使用してシリアル化します。 また Svcutil.exe では、その dataContractOnly スイッチを使用してデータ コントラクトから XML スキーマを生成することもできます。

Note

ASP.NET Web サービスでは XmlSerializer が使用されます。また、WCF ASP.NET 互換モードでは、WCF サービスにおいて ASP.NET Web サービスの動作を真似るようになっていますが、XmlSerializer を使わなければならないわけではありません。 必要であれば ASP.NET 互換モードでも DataContractSerializer も使えるようになっています。

サービスの開発

ASP.NET を使用してサービスを開発する場合、通常は WebService 属性をクラスに追加し、WebMethodAttribute を当該クラスのサービスに対する操作メソッドに追加します。

[WebService]
public class Service : T:System.Web.Services.WebService
{
    [WebMethod]
    public string Echo(string input)
    {
       return input;
    }
}

ASP.NET 2.0 では、WebService 属性や WebMethodAttribute 属性をクラスではなくインターフェイスに追加し、そのインターフェイスを実装するクラスを記述する、という方法も使えるようになりました。

[WebService]
public interface IEcho
{
    [WebMethod]
    string Echo(string input);
}

public class Service : IEcho
{

   public string Echo(string input)
   {
        return input;
    }
}

WebService 属性を持つインターフェイスは、サービスによって実行される操作のコントラクトを構成し、またそれをさまざまなクラスで再利用することによって、同じコントラクトをさまざまな方法で実装できるので、このオプションの使用をお勧めします。

WCF サービスは、WCF エンドポイントをいくつか定義することにより提供されます。 エンドポイントは、アドレス、バインディング、サービス コントラクトで定義します。 アドレスとは、サービスが配備された場所のことです。 バインディングはサービスとの通信方法を表します。 サービス コントラクトとは、サービスが実行できる操作の定義のことです。

サービス コントラクトは通常、インターフェイスに ServiceContractAttribute および OperationContractAttribute を追加して定義します。

[ServiceContract]
public interface IEcho
{
     [OperationContract]
     string Echo(string input);
}

ServiceContractAttribute は、当該インターフェイスによって WCF サービス コントラクトが定義されることを表します。また、OperationContractAttribute は、インターフェイスのどのメソッドによって (存在する場合) サービス コントラクトの操作が定義されるかを表します。

このようにして定義されたサービス コントラクトをクラスとして実装します。サービス コントラクトを定義するインターフェイスを実装するクラスという形で記述します。

public class Service : IEcho
{
    public string Echo(string input)
    {
       return input;
    }
}

サービス コントラクトを実装するクラスは、WCF ではサービスの種類と呼ばれます。

次に、アドレスとバインディングを、サービス型と関連付けます。 そのためには、通常、構成ファイルを直接編集するか、または WCF に付属している構成エディターを使って編集します。 構成ファイルの例を以下に示します。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
     <system.serviceModel>
      <services>
      <service name="Service ">
       <endpoint
        address="EchoService"
        binding="basicHttpBinding"
        contract="IEchoService "/>
      </service>
      </services>
     </system.serviceModel>
</configuration>

バインディングは、アプリケーションとの通信に使う一連のプロトコルを表します。 一般的なシステム指定のバインディングを以下に示します。

名前 目的
BasicHttpBinding WS-BasicProfile 1.1 および Basic Security Profile 1.0 に対応した Web サービスやクライアントとの相互運用性。
WSHttpBinding HTTP 上の WS-* プロトコルに対応した Web サービスやクライアントとの相互運用性。
WSDualHttpBinding 双方向 HTTP 通信。最初のメッセージの受信者は送信元に直接は応答せず、代わりに、WS-* プロトコルに準拠した HTTP で、ある期間にわたって任意の数の応答を送信することができます。
WSFederationBinding HTTP 通信。サービスのリソースに対するアクセスを、明示的に指定された資格情報プロバイダーによって発行された証明書に基づいて制御することができます。
NetTcpBinding ネットワーク全体に分散する WCF ソフトウェア エンティティ間の、安全性や信頼性を確保した高速通信。
NetNamedPipeBinding 同一マシン上の WCF ソフトウェア エンティティ間の、安全性や信頼性を確保した高速通信。
NetMsmqBinding WCF ソフトウェア エンティティ間の、MSMQ を使用した通信。
MsmqIntegrationBinding WCF ソフトウェア エンティティと他のソフトウェア エンティティ間の、MSMQ を使用した通信。
NetPeerTcpBinding WCF ソフトウェア エンティティ間の、Windows ピアツーピア ネットワークを使用した通信。

システム指定のバインディングである BasicHttpBinding には、ASP.NET Web サービスが対応している一連のプロトコルが組み込まれています。

WCF アプリケーション用のカスタム バインディングは、バインディング要素クラスのコレクションとして簡単に定義できます。これは、WCF において、個々のプロトコルを実装する際に使用されます。 追加のプロトコルを表す、新しいバインド要素を記述することも可能です。

サービス型の内部的な動作は、動作を表すクラス群のプロパティで調整できます。 次の例で、ServiceBehaviorAttribute クラスは、サービス型がマルチスレッド処理されることを指定しています。

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator

ServiceBehaviorAttribute など、属性として定義された動作もあります。 これに対し、管理者がプロパティを設定できる動作は、アプリケーションの構成ファイルで変更することができます。

サービス型のプログラムの記述には、多くの場合 OperationContext クラスを使います。 静的プロパティである Current を介して、実行コンテキストに関する情報にアクセスできます。 OperationContext の使い方は、HttpContext クラスや ContextUtil クラスと同様です。

Hosting

ASP.NET Web サービスは、コンパイルしてクラス ライブラリ アセンブリの形になっています。 サービス ファイルという、拡張子が .asmx のファイルの @ WebService ディレクティブに、サービスの実行コードが組み込まれたクラスと、これを収容するアセンブリが定義されています。

<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>

サービス ファイルをインターネット インフォメーション サービス (IIS) の ASP.NET アプリケーション ルート、アセンブリをアプリケーション ルートのサブディレクトリ \bin 以下にコピーすると、 このサービス ファイルの URL (Uniform Resource Locator) でアプリケーションにアクセスできるようになります。

WCF サービスは、IIS 5.1 や 6.0 の他、IIS 7.0 の一部として提供される Windows プロセス アクティブ化サービス (WAS) や、.NET アプリケーション内で容易にホストすることができます。 ただし IIS 5.1/6.0 の場合、通信トランスポート プロトコルは HTTP に限ります。

IIS 5.1/6.0 または WAS 上でサービスをホストする手順を以下に示します。

  1. サービス型をコンパイルしてクラス ライブラリ アセンブリを生成します。

  2. 拡張子を .svc としたサービス ファイルを作り、サービス型を @ ServiceHost ディレクティブで次のように指定します。

    <%@ServiceHost language="c#" Service="MyService" %>

  3. サービス ファイルを仮想ディレクトリにコピーし、アセンブリをその仮想ディレクトリの \bin サブディレクトリにコピーします。

  4. 構成ファイルを仮想ディレクトリに、Web.config という名前でコピーします。

するとアプリケーションには、アプリケーション ルートに置いたサービス ファイルの URL でアクセスできるようになります。

.NET アプリケーション内で WCF サービスをホストする場合は、サービス型をコンパイルしてクラス ライブラリ アセンブリを生成し、これをアプリケーションが参照できるようにします。アプリケーション側では、ServiceHost クラスを使って、サービスをホストできるようプログラムを記述します。 サービスを管理する基本的なプログラムの例を以下に示します。

string httpBaseAddress = "http://www.contoso.com:8000/";
string tcpBaseAddress = "net.tcp://www.contoso.com:8080/";

Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);

Uri[] baseAddresses = new Uri[] {
 httpBaseAddressUri,
 tcpBaseAddressUri};

using(ServiceHost host = new ServiceHost(
typeof(Service), //"Service" is the name of the service type baseAddresses))
{
     host.Open();

     […] //Wait to receive messages
     host.Close();
}

この例では ServiceHost の構築時にトランスポート プロトコルに対してアドレスを指定しています。 このアドレスをベース アドレスと呼びます。

WCF サービスのエンドポイントに提供されるアドレスは、エンドポイントのホストに設定されたベース アドレスから見た相対アドレスです。 ホストではトランスポート プロトコルごとに 1 つベース アドレスを設定できます。 上記の構成ファイルの構成例では、エンドポイントに対して選択された BasicHttpBinding はトランスポート プロトコルとして HTTP を使用しているので、エンドポイント EchoService のアドレスは、HTTP に対して設定されたベース アドレスを基準としたものになります。 上記の例に挙げたホストの場合、HTTP ベース アドレスは http://www.contoso.com:8000/ です。 なお、IIS や WAS 上でホストされているサービスの場合、ベース アドレスはサービス ファイルの URL になります。

WCF を ASP.NET 互換モードで使用できるのは、IIS や WAS 内でホストされていて、トランスポート プロトコルとしてもっぱら HTTP を使うサービスに限られます。 ASP.NET 互換モードは、次の 2 つの手順で切り替えます。

  1. プログラムの開発者は、サービス型に AspNetCompatibilityRequirementsAttribute 属性を追加し、ASP.NET 互換モードに切り替えることができる、または切り替えることが必須である旨の指定をしておく必要があります。

    [System.ServiceModel.Activation.AspNetCompatibilityRequirements(
          RequirementsMode=AspNetCompatibilityRequirementsMode.Require)]
    public class DerivativesCalculatorServiceType: IDerivativesCalculator
    
  2. 管理者は、アプリケーションが ASP.NET 互換モードで動作するよう、次のように構成する必要があります。

    <configuration>
         <system.serviceModel>
          <services>
          […]
          </services>
          <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        </system.serviceModel>
    </configuration>
    

    WCF アプリケーションは、サービス ファイルの拡張子として .svc ではなく .asmx を使用するように構成することも可能です。

    <system.web>
         <compilation>
          <compilation debug="true">
          <buildProviders>
           <remove extension=".asmx"/>
           <add extension=".asmx"
            type="System.ServiceModel.ServiceBuildProvider,
            System.ServiceModel,
            Version=3.0.0.0,
            Culture=neutral,
            PublicKeyToken=b77a5c561934e089" />
          </buildProviders>
          </compilation>
         </compilation>
    </system.web>
    

    こうすると、.asmx サービス ファイルの URL を使用するように構成されたクライアントを変更しなくても、サービスは WCF を使用するようになります。

クライアント開発

ASP.NET Web サービスのクライアントの開発にはコマンド ライン ツール WSDL.exe を使用します。.asmx ファイルの URL を入力として指定します。 WCF によって提供される対応ツールは、ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe) です。 これにより、サービス コントラクトおよび WCF クライアント クラスの定義から、コード モジュールが生成されます。 また、サービスのアドレスとバインディングを指定して、構成ファイルを生成することもできます。

リモート サービスのクライアントを開発する場合、通常は、非同期パターンに従ってプログラムを記述するようお勧めします。 WSDL.exe ツールは、特段の指定をしなくても、同期パターンと非同期パターンを使ったコードをそれぞれ生成します。 ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe) によって生成されるコードは、どちらのパターンにも対応できます。 特に指定しなければ同期パターン用です。 /async スイッチを指定して実行すれば、生成されるコードは非同期パターン用になります。

既定では、ASP.NET の WSDL.exe ツールで生成した WCF クライアント クラスの名前が、Svcutil.exe ツールで生成した WCF クライアント クラスの名前と一致するとは限りません。 特に、XmlSerializer でシリアル化したクラスのプロパティ名は、Svcutil.exe で生成した場合 "Property" という接頭辞が付きますが、WSDL.exe の場合はそうなりません。

メッセージ表現

ASP.NET Web サービスとやり取りする SOAP メッセージのヘッダーはカスタマイズ可能です。 SoapHeader の派生クラスでヘッダーの構造を定義し、SoapHeaderAttribute でヘッダーが存在することを指定します。

public class SomeProtocol : SoapHeader
{
     public long CurrentValue;
     public long Total;
}

[WebService]
public interface IEcho
{
     SomeProtocol ProtocolHeader
     {
      get;
     set;
     }

     [WebMethod]
     [SoapHeader("ProtocolHeader")]
     string PlaceOrders(PurchaseOrderType order);
}

public class Service: WebService, IEcho
{
     private SomeProtocol protocolHeader;

     public SomeProtocol ProtocolHeader
     {
         get
         {
              return this.protocolHeader;
         }

         set
         {
              this.protocolHeader = value;
         }
     }

     string PlaceOrders(PurchaseOrderType order)
     {
         long currentValue = this.protocolHeader.CurrentValue;
     }
}

WCF には、サービスとやり取りする SOAP メッセージの構造を記述する、MessageContractAttributeMessageHeaderAttributeMessageBodyMemberAttribute などの属性があります。

[DataContract]
public class SomeProtocol
{
     [DataMember]
     public long CurrentValue;
     [DataMember]
     public long Total;
}

[DataContract]
public class Item
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}

[MessageContract]
public class ItemMessage
{
     [MessageHeader]
     public SomeProtocol ProtocolHeader;
     [MessageBody]
     public Item Content;
}

[ServiceContract]
public interface IItemService
{
     [OperationContract]
     public void DeliverItem(ItemMessage itemMessage);
}

この構文でメッセージ構造を明示的に記述できますが、ASP.NET Web サービスのコードからメッセージ構造を導くことも可能です。 ASP.NET の構文では、メッセージ ヘッダーは前述の例の ProtocolHeader プロパティのようにサービスのプロパティとして表現されますが、WCF の構文ではメッセージのプロパティとしてより厳密に表現されます。 WCF ではさらに、エンドポイントの構成にメッセージ ヘッダーを追加することも可能です。

<service name="Service ">
     <endpoint
      address="EchoService"
      binding="basicHttpBinding"
      contract="IEchoService ">
      <headers>
      <dsig:X509Certificate
       xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
       ...
      </dsig:X509Certificate>
      </headers>
     </endpoint>
</service>

この方法では、クライアントやサービスのコード内で、基盤となるプロトコル ヘッダーを参照しなくても済みます。エンドポイントが適切に構成されているので、ヘッダーがメッセージ中に取り込まれるからです。

サービスの説明

WSDL で記述したクエリを HTTP GET 要求として発行して、ASP.NET Web サービスの .asmx ファイルを取得しようとすると、ASP.NET は WSDL によるサービス記述を生成し、 要求に対する応答として返します。

ASP.NET 2.0 では、サービスが Web Services-Interoperability Organization (WS-I) の Basic Profile 1.1 に準拠していることを検証し、その旨を WSDL による記述に追加できるようになりました。 この処理には、ConformsTo 属性の EmitConformanceClaims および WebServiceBindingAttribute パラメーターを使用します。

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
     ConformsTo = WsiProfiles.BasicProfile1_1,
     EmitConformanceClaims=true)]
public interface IEcho

ASP.NET が WSDL で生成したサービス記述はカスタマイズ可能です。 ServiceDescriptionFormatExtension の派生クラスを作成し、WSDL による記述に項目を追加する、という形でカスタマイズします。

WSDL で記述したクエリを HTTP GET 要求として発行し、IIS 5.1/6.0 または WAS でホストされていて HTTP エンドポイントを持つ WCF サービスの .svc ファイルを取得しようとすると、WCF では WSDL によるサービス記述が生成されて返されます。 httpGetEnabled が true に設定されている場合は、WSDL で記述したクエリを HTTP GET 要求として、.NET アプリケーション上でホストされているサービスの HTTP ベース アドレスに発行しても同じ効力があります。

一方、WCF では、WS-MetadataExchange 要求に対しても、WSDL によるサービス記述が生成されて返されます。 ASP.NET Web サービスには、WS-MetadataExchange 要求に応答する機能がありません。

WCF によって生成された WSDL は、広範なカスタマイズが可能です。 ServiceMetadataBehavior クラスには、WSDL による記述をカスタマイズするための機能がいくつか組み込まれています。 WCF も、WSDL を生成する代わりに、所定の URL に置いた静的な WSDL ファイルを使用するように構成できます。

<behaviors>
     <behavior name="DescriptionBehavior">
     <metadataPublishing
      enableMetadataExchange="true"
      enableGetWsdl="true"
      enableHelpPage="true"
      metadataLocation=
      "http://localhost/DerivativesCalculatorService/Service.WSDL"/>
     </behavior>
</behaviors>

例外処理

ASP.NET Web サービスでは、処理できない例外が発生すると、SOAP エラーとしてクライアントに返されます。 また、SoapException クラスのインスタンスを明示的にスローして、クライアント側に SOAP エラーの詳しい状況を通知し、より適切に管理させることも可能です。

WCF サービスでは、処理できない例外が SOAP エラーとしてクライアントに返されることはありません。例外を通して、重要な情報が不用意に表示されるのを防ぐためです。 ただしデバッグ目的で、このような例外をクライアントに返すように設定することは可能です。

SOAP エラーをクライアントに返す場合、データ コントラクト型を汎用型 FaultException<TDetail> のインスタンスとしてキャストし、これをスローする方法が使えます。 また、操作に FaultContractAttribute 属性を追加して、操作で生じうるエラーを指定する方法もあります。

[DataContract]
public class MathFault
{
     [DataMember]
     public string operation;
     [DataMember]
     public string problemType;
}

[ServiceContract]
public interface ICalculator
{
     [OperationContract]
     [FaultContract(typeof(MathFault))]
     int Divide(int n1, int n2);
}

これにより、サービスで発生する可能性のあるエラーが WSDL の形で公開されます。クライアント側の開発者は、当該操作によりどのようなエラーが生じうるかをあらかじめ予測し、catch ブロック内に適切な処理を組み込んでおくことができます。

try
{
     result = client.Divide(value1, value2);
}
catch (FaultException<MathFault> e)
{
 Console.WriteLine("FaultException<MathFault>: Math fault while doing "
  + e.Detail.operation
  + ". Problem: "
  + e.Detail.problemType);
}

状態管理

ASP.NET Web サービスの実装には WebService の派生クラスを使うこともできます。

public class Service : WebService, IEcho
{

 public string Echo(string input)
 {
  return input;
 }
}

この場合、基本クラス WebService に定義された Context プロパティを使って、HttpContext オブジェクトにアクセスすることになります。 HttpContext オブジェクトには、アプリケーションやセッションの状態情報をそれぞれ更新、取得する、Application プロパティ、Session プロパティがあります。

ASP.NET では、HttpContext の Session プロパティでアクセスするセッション状態情報の、実際の格納場所を細かく制御できます。 格納場所としては、クッキー内、データベース内、稼動中のサーバーのメモリ上、状態管理用の特別なサーバーのメモリ上があり、 サービスの構成ファイルで指定します。

WCF には、状態管理用に拡張可能なオブジェクトが用意されています。 いずれも IExtensibleObject<T> を実装しています。 中でも重要な拡張可能オブジェクトは、ServiceHostBase および InstanceContext です。 ServiceHostBase を使用すると、同一ホスト上の全サービス型のあらゆるインスタンスからアクセスできる状態を管理できます。一方、InstanceContext を使用すると、同じサービス型のインスタンス内で実行されるコードからアクセスできる状態を管理できます。

ここでは、サービス型 TradingSystemServiceBehaviorAttribute という属性があります。これは、同じ WCF クライアント インスタンスからの呼び出しはすべて、同じサービス型のインスタンスに転送されることを表します。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TradingSystem: ITradingService

次のクラス DealData に定義されている状態には、同じサービス型のインスタンス内で実行されるどのコードからでもアクセスできます。

internal class DealData: IExtension<InstanceContext>
{
 public string DealIdentifier = null;
 public Trade[] Trades = null;
}

サービス コントラクトの操作のいずれかを実装するサービス型のコードでは、状態オブジェクト DealData を、当該サービス型の現在のインスタンスに関する状態として追加することができます。

string ITradingService.BeginDeal()
{
 string dealIdentifier = Guid.NewGuid().ToString();
 DealData state = new DealData(dealIdentifier);
 OperationContext.Current.InstanceContext.Extensions.Add(state);
 return dealIdentifier;
}

この状態オブジェクトは、サービス コントラクトの他の操作を実装するコードから取得、更新できます。

void ITradingService.AddTrade(Trade trade)
{
 DealData dealData =  OperationContext.Current.InstanceContext.Extensions.Find<DealData>();
 dealData.AddTrade(trade);
}

ASP.NET では HttpContext クラスで管理する状態情報の実際の格納場所を制御できますが、WCF の場合、少なくとも初期バージョンのままでは、拡張可能オブジェクトの保存場所を制御できません。 これも WCF サービスの ASP.NET 互換モードを推奨する理由の 1 つです。 このような制御が不可欠な応用の場合、ASP.NET 互換モードにすれば、ASP.NET と同様に HttpContext クラスの機能を活用できるばかりでなく、HttpContext クラスで管理する状態情報の実際の格納場所も制御できます。

セキュリティ

ASP.NET Web サービスのセキュリティ保全の手順は、IIS アプリケーションのセキュリティ保全の手順と同じです。 WCF アプリケーションは IIS に限らずどんな .NET 実行可能ファイル内でもホストできるので、WCF アプリケーションのセキュリティ保全のためのオプションも IIS の機能に依存しないものにする必要があります。 ただし、ASP.NET Web サービスによって提供される機能は、WCF サービスからも、ASP.NET 互換モードで実行していれば利用できます。

セキュリティ : 認証

IIS にはアプリケーションへのアクセス制御機能が組み込まれており、匿名アクセスの他、Windows 認証、ダイジェスト認証、基本認証、.NET パスポート認証など、さまざまな認証モードを切り替えることができます。 Windows 認証は、ASP.NET Web サービスへのアクセス制御に使えます。 ただし、WCF アプリケーションを IIS 内でホストする場合、アプリケーションへの匿名アクセスを許可するように IIS を構成し、認証は他のさまざまなオプションの中から Windows 認証をサポートしている WCF で行うようにする必要があります。 他の認証方法としては、ユーザー名トークン、X.509 証明書、SAML トークン、CardSpace カードなどが組み込まれていますが、独自の認証機構を定義することも可能です。

セキュリティ : 偽装

ASP.NET Web サービスは ASP.NET の ID 要素を使用して、あるユーザーに偽装することができます。あらかじめ設定した特定のユーザーでなくても、要求に資格情報が添えられていれば、その要求元ユーザーに偽装できます。 ASP.NET 互換モードで動作する WCF アプリケーションでは、この要素を使って偽装を構成できます。

WCF の構成システムでは、偽装するユーザーを指定するための独自の ID 要素が用意されています。 また、WCF のクライアント側とサービス側で、別々に偽装を構成できます。 クライアント側では、要求を送信する現在のユーザーに偽装する構成が可能です。

<behaviors>
     <behavior name="DerivativesCalculatorClientBehavior">
      <clientCredentials>
      <windows allowedImpersonationLevel="Impersonation"/>
      </clientCredentials>
     </behavior>
</behaviors>

サービス側では、現在の要求と共に提供された資格情報のユーザーに偽装するように構成できます。

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void Receive(Message input)

セキュリティ : アクセス制御リストによる承認

アクセス制御リスト (ACL) を使って .asmx ファイルへのアクセスを制限できます。 ただし、ASP.NET 互換モードでない限り、WCF の .svc ファイル上の ACL は無視されます。

セキュリティ : ロール ベースの承認

IIS の Windows 認証オプションを、ASP.NET 構成言語の承認要素と組み合わせて使用すると、ASP.NET Web サービスに対し、各ユーザーが属する Windows グループに基づくロール ベースの承認機構を提供できます。 ASP.NET 2.0 には、より汎用的なロール ベースの承認機構である、ロール プロバイダーが導入されました。

ロール プロバイダーとは、ユーザーが割り当てられたロールに関する問い合わせを行う、基本インターフェイスを実装したクラス群です。各ロール プロバイダーには、さまざまな情報源から必要な情報を取得する手段が組み込まれています。 ASP.NET 2.0 には、Microsoft SQL Server データベースからロールの割り当てを検索できるロール プロバイダーと、Windows Server 2003 承認マネージャーから検索できるロール プロバイダーがあります。

WCF アプリケーションを含む .NET アプリケーションでは、ロール プロバイダーの機構を ASP.NET とは別に使用できます。 WCF アプリケーション用の構成例を以下に示します。このように、ASP.NET のロール プロバイダーの使い方は、ServiceAuthorizationBehavior を使用して選択できます。

<system.serviceModel>
     <services>
         <service name="Service.ResourceAccessServiceType"
             behaviorConfiguration="ServiceBehavior">
             <endpoint
              address="ResourceAccessService"
              binding="wsHttpBinding"
              contract="Service.IResourceAccessContract"/>
         </service>
     </services>
     <behaviors>
       <behavior name="ServiceBehavior">
       <serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
      </behavior>
     </behaviors>
</system.serviceModel>

セキュリティ : クレーム ベースの承認

WCF で最も重要な革新的な機能の 1 つは、保護されたリソースへのアクセスを、クレーム ベースで承認できるという点です。 クレームは、型、権限、および値で構成されます。たとえば、運転免許証を考えてみましょう。 ここには所持者に関する誕生日などの情報 (ここでいう「クレーム」) が記載されています。 つまり、クレームの型は「誕生日」、値は運転者の実際の誕生日です。 また、クレームの権限は、所持者がこの値に対してできることを表します。 誕生日について言えば、所持者はこの情報を「見る」ことはできますが、「書き換える」ことはできません。 クレーム ベースの承認は、ロール ベースの承認を包含する概念です。というのも、ロールはクレームの 1 つの型であると考えることができるからです。

クレーム ベースの承認では、一連のクレームを操作のアクセス要求と比較し、その結果に応じてアクセスを許可または拒否します。 WCF では、ServiceAuthorizationBehaviorServiceAuthorizationManager プロパティに値を割り当てることで、クレーム ベースの承認の実行に使用するクラスを指定できます。

<behaviors>
     <behavior name='ServiceBehavior'>
     <serviceAuthorization
     serviceAuthorizationManagerType=
                   'Service.AccessChecker, Service' />
     </behavior>
</behaviors>

クレーム ベースの承認を行うクラスは、ServiceAuthorizationManager を継承し、AccessCheck() メソッドだけをオーバーライドして定義します。 WCF では、サービスの操作が起動されたときに、このメソッドが呼び出され、ServiceSecurityContext.AuthorizationContext プロパティにユーザーへのクレームを含む OperationContext オブジェクトが提供されます。 WCF では、認証のためにユーザーから提供されたセキュリティ トークンからユーザーに関するクレームが収集されます。このため、これらのクレームが該当する操作に十分であるかどうかを評価するタスクが残されます。

あらゆるセキュリティ トークンから自動的にクレームを収集できることが、WCF の革新的な機能の 1 つです。これにより、認証機構とは無関係に、クレーム ベースの承認を行うコードを作成できます。 これに対し、ACL やロール ベースの承認は、Windows 認証と密に関連し合っています。

セキュリティ : 機密性

ASP.NET Web サービスとの間でやり取りするメッセージの機密性は、トランスポート層で、IIS 上のアプリケーションが Secure Hypertext Transfer Protocol (HTTPS) を使うように構成することによって確保します。 IIS 内でホストされている WCF アプリケーションについても同様です。 ただし、IIS 以外でホストされている WCF アプリケーションも、安全なトランスポート プロトコルを使うように構成できます。 さらに、WCF アプリケーションは、メッセージを転送する前に WS-Security プロトコルを使用してそれを保護するように構成することも可能です。 メッセージの本体を WS-Security で保護することにより、最終送信先に到達するまでの中継ノードで機密が洩れないようにすることができます。

グローバリゼーション

ASP.NET 構成言語では、個々のサービスごとにカルチャを指定することができます。 WCF でこの設定ができるのは、ASP.NET 互換モードの場合に限ります。 ASP.NET 互換モードを使用しない WCF サービスをローカライズするためには、サービス型をコンパイルしてカルチャごとのアセンブリを生成し、エンドポイントもカルチャごとのアセンブリそれぞれについて用意する必要があります。

関連項目