Craig McMurtry
テクニカル エバンジェリスト
Microsoft Corporation
2006 年 7 月
日本語版最終更新日 2006 年 11 月 20 日
該当製品:
Microsoft ASP.NET 2.0
Windows Communication Foundation
Internet Information Services (IIS)
Web サービス仕様
目次
意思決定者の視点
テクノロジの比較: 目的
テクノロジの比較: 標準
テクノロジの比較: 開発
内部アーキテクチャ
WCF 導入の準備: 将来的な統合の容易化
WCF 導入の準備: 将来的な移行の容易化
WCF の導入
まとめ
概要: この記事では、ASP.NET Web サービスを Windows Communication Foundation (WCF) と比較し、WCF のリリースが近づきつつある現在、既存および計画中の ASP.NET Web サービスをどう扱うかについて説明します。
ASP.NET は、Web サービス構築用の .NET Framework クラス ライブラリおよびツールを提供すると共に、これらのサービスを Internet Information Services (IIS) でホスティングするための機能を提供します。 Windows Communication Foundation (以前のコードネーム Indigo、以下 WCF) は、Web サービスで使用するプロトコルも含めて、任意のプロトコルによるソフトウェア エンティティ間の通信を可能にする .NET クラス ライブラリ、ツール、およびホスティング機能を提供します。 このように、Web サービス構築のための新しい、より汎用的なテクノロジが出現しつつある今、ASP.NET Web サービスは、どのような対応をすべきでしょうか。
この資料では、これら 2 つのテクノロジを比較し、 ASP.NET Web サービス アプリケーションを WCF アプリケーションと併用する方法について説明します。 また、ASP.NET Web サービスの WCF への移行を準備する方法、およびその移行を実際に行う方法についても説明します。
この資料では、ASP.NET で提供される Web サービス構築機能を、Web Services Enhancements for Microsoft .NET (WSE) とは別のものと見なします。 WSE で開発したアプリケーションの今後については、別の資料で検証します。
意思決定者の視点
2006 年後半にリリースが予定されている WCF には、ASP.NET Web サービスに比較して重要な利点がいくつかあります。この旧来のテクノロジを利用しているすべての企業が、これらの利点について検討する必要があります。
ASP.NET Web サービスのツールは Web サービスの構築のみを目的としているのに対し、WCF では、ソフトウェア エンティティを相互に通信させる必要のある、あらゆる状況で使用するツールが提供されます。 したがって、開発者がさまざまなソフトウェア通信シナリオに対応するうえで、知っておく必要のあるテクノロジの数が削減されます。結果的に、ソフトウェア開発リソースのコストを節約し、ソフトウェア開発プロジェクトの所要時間を短縮できます。
Web サービス開発プロジェクトにおいても、WCF は ASP.NET Web サービスよりも多くの Web サービス プロトコルをサポートしています。 これらのプロトコルは、信頼性の高いセッションやトランザクションをはじめ、より高度なソリューションを実現します。
WCF は、ASP.NET Web サービスよりも多くのメッセージ転送プロトコルをサポートしています。 ASP.NET Web サービスでは、Hypertext Transfer Protocol (HTTP) によるメッセージの送信しかサポートされません。 一方 WCF は HTTP でのメッセージ送信だけでなく、Transmission Control Protocol (TCP)、名前付きパイプ、および Microsoft Message Queuing (MSMQ) でのメッセージ送信にも対応しています。 さらに重要な点として、WCF では、トランスポート プロトコルのサポートを容易に拡張できます。 したがって、WCF で開発したソフトウェアは、広範囲に及ぶ他のソフトウェアと連携するよう容易に変更できます。結果的に WCF への投資を大きく回収できる可能性があります。
WCF には、アプリケーションを展開して管理するための機能が、ASP.NET Web サービスよりもはるかに豊富に用意されています。 ASP.NET でも提供されている構成システムに加えて、WCF では構成エディタ、任意の数の中間ノードを経由する送信側から受信側までのアクティビティ トレース、トレース ビューア、メッセージ ログ、各種のパフォーマンス カウンタ、および Windows Management Instrumentation のサポートが提供されています。 このような豊富な管理ツールの集合によって、運用コストの削減、障害リスクの低減、実際のダウンタイムの短縮が可能となります。
以上のように WCF は、ASP.NET Web サービスと比較して有利であると見込まれることから、ASP.NET Web サービスを現在使用している、または使用を検討している企業の対応としては、いくつかの選択肢があります。
- このまま ASP.NET Web サービスを使い続け、WCF で提供される利点については見送る。 マイクロソフトの現在のサポート ライフサイクル ポリシーでは、ASP.NET Web サービスのメインストリーム サポートは少なくとも 2011 年まで続行され、延長サポートは少なくとも 2016 まで提供される予定です。したがって、ASP.NET Web サービスを使い続けても大きなリスクはありません。
- 将来いずれかの時点で WCF を導入することを前提に、ASP.NET Web サービスを使い続ける。 この資料では、新しい ASP.NET Web サービス アプリケーションを今後の WCF アプリケーションと併用できる可能性を最大限にする方法について説明します。 また、新しい ASP.NET Web サービスを、WCF に移行しやすい形で構築する方法についても説明します。 ただし、サービスの保護が重要である場合、信頼性やトランザクションの保証が要求される場合、またはカスタムな管理機能を構築する必要が生じる見込みがある場合には、WCF の導入を先送りにするのは、間違った判断かもしれません。 WCF は、まさにそのようなシナリオを設計目標としており、このテクノロジのプロダクション ライセンスは既に取得可能です。
- 新規の開発作業には WCF を採用しながら、既存の ASP.NET Web サービス アプリケーションのメンテナンスを続行する。 この選択肢がおそらく最適でしょう。 この場合、WCF の利点を活用すると同時に、既存のアプリケーションで WCF を使用するために必要となる変更のコストを節約できます。 このシナリオでは、新しい WCF アプリケーションを、既存の ASP.NET アプリケーションと共存させることが可能です。 またWCF アプリケーションで既存の ASP.NET Web サービスを使用することができ、さらに WCF の ASP.NET 互換モードを利用して、既存の ASP.NET アプリケーションの新しい運用機能を WCF でプログラミングすることも可能です。
- WCF を導入し、既存の ASP.NET Web サービス アプリケーションを WCF に移行する。 企業によっては、開発者が ASP.NET Web サービスについて知らなくても済むようにする目的で、この選択肢を選ぶケースが考えられます。 ただし、最新のテクノロジ以外のテクノロジのサポートを排除できるという見通しはおそらく非現実的なので、この選択肢を選ぶ理由として適切ではありません。 移行の理由として唯一適切なのは、WCF で提供される機能によって既存のアプリケーションの機能を強化するという目的、または新しくより強力な WCF アプリケーションで既存の ASP.NET Web サービス機能を再現するという目的です。 この資料では、移行の実現方法について説明します。
テクノロジの比較: 目的
ASP.NET Web サービス テクノロジは、HTTP 上の Simple Object Access Protocol (SOAP) を使用してメッセージを送受信するアプリケーションを構築するために開発されました。 メッセージの構造は XML スキーマを使用して定義することができ、.NET オブジェクトとやり取りするメッセージのシリアル化を容易にするツールが提供されています。 このテクノロジでは、Web サービスを記述するメタデータを Web Services Description Language (WSDL) で自動生成することができ、WSDL から Web サービスのクライアントを生成するための 2 つ目のツールが提供されています。
WCF は、.NET アプリケーションが他のソフトウェア エンティティとメッセージを交換できるようにすることを目的としています。 既定では SOAP が使用されますが、メッセージは任意の形式とすることができ、任意のトランスポート プロトコルで伝送できます。 メッセージの構造は XML スキーマで定義することができ、.NET オブジェクトとやり取りするメッセージをシリアル化するための各種オプションがあります。 WSDL のテクノロジを使用して構築したアプリケーションを記述するメタデータを WCF で自動生成することができ、WSDL からこれらのアプリケーションのクライアントを生成するためのツールも提供されています。
テクノロジの比較: 標準
ASP.NET Web サービスでサポートされる標準の一覧は、ASP.NET を使用して作成した XML Web サービス (英語) を参照してください。
WCF では、より多くの標準がサポートされています。これは WCF でサポートされる Web サービス プロトコル (英語) で確認できます。
テクノロジの比較: 開発
ASP.NET 互換モード
WCF には ASP.NET 互換モードがあり、ある種の WCF アプリケーションを ASP.NET Web サービスと同じようにプログラミングおよび構成し、ASP.NET Web サービスの動作を模倣することができます。 ASP.NET 互換モードを選択する方法と、このオプションによる実際の作用について、後で詳しく説明します。
データの表記
一般に ASP.NET による Web サービスの開発作業は、そのサービスで使用する複雑なデータ型を定義することから始まります。 ASP.NET では System.Xml.Serialization.XmlSerializer に依存して、.NET オブジェクトで表記されたデータを XML に変換してサービスとの間でやり取りしたり、XML で受信したデータを .NET オブジェクトに変換したりします。 したがって、ASP.NET サービスで使用する複雑なデータ型を定義するには、System.Xml.Serialization.XmlSerializer が XML との間でシリアル化できるような .NET クラスの定義が必要です。 このようなクラスは、もちろん手動で作成することもできますが、コマンドライン方式の XML スキーマ/データ型サポート ユーティリティである xsd.exe を使用して、XML スキーマ内の型定義から生成することもできます。
System.Xml.Serialization.XmlSerializer が XML との間でシリアル化できる .NET クラスの定義について、知っておく必要のある重要事項は次のとおりです。
- XML に変換されるのは、.NET オブジェクトのパブリックなフィールドおよびプロパティだけです。
- 集合クラスのインスタンスを XML にシリアル化できるのは、そのクラスが IEnumerable または ICollection インターフェイスを実装している場合に限られます。
- 必然的に、System.Collections.IDictionary インターフェイスを実装しているクラスは、System.Collections.Hashtable と同様、XML にシリアル化できません。
- System.Xml.Serialization 名前空間内の多くの属性タイプを .NET クラスおよびそのメンバーに追加して、XML におけるクラスのインスタンスの表現方法を正確に制御することができます。
WCF アプリケーションの開発も、複雑な型の定義から始まるのが一般的です。 WCF でも、ASP.NET Web サービスと同じ .NET 型の使用が可能です。 ただし、それよりも優れた代替手段があります。
WCF の System.Runtime.Serialization アセンブリの System.Runtime.Serialization.DataContract および System.Runtime.Serialization.DataMember 属性を .NET 型に追加することにより、その型のインスタンスが XML にシリアル化されることを表すと共に、シリアル化の対象となる特定のフィールドまたはプロパティを指定することができます。 次に示す 3 つの例は、いずれも有効です。
//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;
}
}
}
System.Runtime.Serialization.DataContract 属性は、型の 0 個以上のフィールドまたはプロパティをシリアル化することを表すのに対し、System.Runtime.Serialization.DataMember 属性は、特定のフィールドまたはプロパティをシリアル化することを表します。 System.Runtime.Serialization.DataContract 属性は、クラスまたは構造体に適用できます。 System.Runtime.Serialization.DataMember 属性は、フィールドまたはプロパティに適用できます。この属性を適用するフィールドおよびプロパティは、パブリックでもプライベートでも構いません。 System.Runtime.Serialization.DataContract 属性を持つ型のインスタンスのことを、WCF の用語でデータ コントラクトといいます。 これらは、WCF の System.Runtime.Serialization.DataContractFormatter を使用して XML にシリアル化されます。
System.Runtime.Serialization.DataContractFormatter、System.Runtime.Serialization.DataContract、および System.Runtime.Serialization.DataMember 属性は、System.Xml.Serialization.XmlSerializer および System.Xml.Serialization 名前空間の各種の属性と、どのように違うのでしょうか。 多くの重要な相違点があります。
- System.Xml.Serialization.XmlSerializer および System.Xml.Serialization 名前空間の属性は、.NET 型を、XML スキーマで定義済みの有効な型にマッピングできるようにする目的で設計されています。そのため、XML での .NET 型の表現方法を、非常に正確に制御できるようになっています。 System.Runtime.Serialization.DataContractFormatter、System.Runtime.Serialization.DataContract、および System.Runtime.Serialization.DataMember 属性では、XML における .NET 型の表現方法について、ほとんど制御することができません。 指定できるのは、型とそのフィールドまたはプロパティを表すために XML で使用する名前空間と名前、および XML での各フィールドとプロパティの順序だけです。
[DataContract(
Namespace="urn:Woodgrove: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;
}
.NET 型の表現に使用する XML の構造について、これ以外の事項はすべて、System.Runtime.Serialization.DataContractFormatter によって決定されます。
- XML での .NET 型の表現方法がほとんど制御できないようになっているため、シリアル化プロセスが System.Runtime.Serialization.DataContractFormatter にとって非常に予測しやすいものになり、結果的に、最適化しやすいものになっています。 したがって、System.Runtime.Serialization.DataContractFormatter の設計による実用上の利点はパフォーマンスの向上であり、約 10% 高いパフォーマンスが得られます。
- たとえば次の型では、System.Xml.Serialization.XmlSerializer によるシリアル化の属性を指定しています。
[Serializable]
[XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
public class LineItem
{
public string ItemNumber;
public decimal Quantity;
public decimal UnitPrice;
}
一方、それと比較して、次のバージョンでは System.Runtime.Serialization.DataContractFormatter で使用する属性を指定しています。
[DataContract(Namespace="urn:Woodgrove:2006:January:29")]
public class LineItem
{
[DataMember]
public string ItemNumber;
[DataMember]
public decimal Quantity;
[DataMember]
public decimal UnitPrice;
}
System.Xml.Serialization.XmlSerializer で使用する属性では、型のどのフィールドまたはプロパティを XML にシリアル化するかを指定していません。一方、System.Runtime.Serialization.DataContractFormatter に使用する System.Runtime.Serialization.DataMember 属性では、シリアル化するフィールドまたはプロパティを明示的に指定しています。 したがって、データ コントラクトは、アプリケーションが送受信するデータの構造に関する明示的な規約であると言えます。
- System.Xml.Serialization.XmlSerializer では .NET オブジェクトのパブリック メンバーしか XML に変換できないのに対し、System.Runtime.Serialization.DataContractFormatter は、メンバーのアクセス修飾子にかかわらず、.NET オブジェクトのメンバーを XML に変換できます。
- 型の非パブリック メンバーを XML にシリアル化可能であることの結果でもありますが、System.Runtime.Serialization.DataContractFormatter では、XML にシリアル化できる .NET 型の種類に関する制限が少なくなっています。 具体的には、Systems.Collections.IDictionary インターフェイスを実装する System.Collections.Hashtable のような型が XML に変換可能です。 一般に、既存の .NET 型のインスタンスを、型定義を修正したり、その型用のラッパーを作成しなくても、System.Runtime.Serialization.DataContractFormatter で XML にシリアル化できる可能性が高くなっています。
- ただし、System.Runtime.Serialization.DataContractFormatter では、型の非パブリック メンバーにアクセス可能であることのもう 1 つの結果として、System.Xml.Serialization.XmlSerializer とは異なり、完全な信頼が必要です。
- System.Runtime.Serialization.DataContractFormatter には、ある程度までのバージョン管理のサポートが組み込まれています。
- System.Runtime.Serialization.DataMember 属性には IsRequired プロパティがあります。データ コントラクトの旧バージョンには存在しなかったメンバーを新バージョンに追加する場合、そのメンバーに対してこのプロパティに false の値を割り当てることにより、コントラクトの新バージョンを使用するアプリケーションで旧バージョンを処理可能にすることができます。
- データ コントラクトに単純な System.Runtime.Serialization.IExtensibleDataObject インターフェイスを実装することにより、新バージョンのデータ コントラクトで定義したメンバーを System.Runtime.Serialization.DataContractFormatter によって旧バージョンのコントラクトを使用するアプリケーションに渡すことができます。
以上のような相違があるとはいえ、XML の名前空間が明示的に定義されている場合、System.Xml.Serialization.XmlSerializer が既定で型をシリアル化する XML は、System.RuntimeSerialization.DataContractFormatter が型をシリアル化する XML と意味的に同じです。 したがって、次のクラスは、両方のシリアライザで使用される属性を持っているので、System.Xml.Serialization.XmlSerializer でも System.RuntimeSerialization.DataContractFormatter でも、意味的に同じ XML に変換されます。
[Serializable]
[XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
[DataContract(Namespace="urn:Woodgrove:2006:January:29")]
public class LineItem
{
[DataMember]
public string ItemNumber;
[DataMember]
public decimal Quantity;
[DataMember]
public decimal UnitPrice;
}
WCF に付属のソフトウェア開発キットには、サービス モデル メタデータ ツールと呼ばれるコマンドライン ツール、svcutil.exe が含まれています。 ASP.NET Web サービスで使用する xsd.exe ツールと同じように、svcutil.exe は XML スキーマから .NET データ型の定義を生成できます。 これらの .NET データ型は、System.Runtime.Serialization.DataContractFormatter が XML スキーマで定義される形式の XML を生成できる場合には、データ コントラクトになります。そうでない場合には、System.Xml.Serialization.XmlSerializer を使用してシリアル化することが意図されています。 svcutil.exe ツールでは、/dataContractOnly スイッチを使用してデータ コントラクトから XML スキーマを生成することも可能です。
ASP.NET Web サービスが System.Xml.Serialization.XmlSerializer を使用しており、なおかつ WCF の ASP.NET 互換モードでは WCF サービスが ASP.NET Web サービスの動作を模倣するとしても、ASP.NET 互換オプションで必ず System.Xml.Serialization.XmlSerializer を使用しなければならないわけではありません。 この場合にも、ASP.NET 互換モードで動作するサービスに System.Runtime.Serialization.DataContractFormatter を使用することができます。
サービスの開発
ASP.NET でサービスを開発する場合、単純にクラスに System.Web.Services.WebService 属性を追加し、そのクラスのメソッドのうちサービスの動作となるメソッドに System.Web.Services.WebMethod 属性を追加することが慣例となっています。
[WebService]
public class Service : System.Web.Services.WebService
{
[WebMethod]
public string Echo(string input)
{
return input;
}
}
ASP.NET 2.0 では、クラスではなくインターフェイスに System.Web.Services.WebService および System.Web.Services.WebMethod 属性を追加し、そのインターフェイスを実装するクラスを記述するというオプションが導入されました。
[WebService]
public interface IEcho
{
[WebMethod]
string Echo(string input);
}
public class Service : IEcho
{
public string Echo(string input)
{
return input;
}
}
このオプションを使用する方が望ましい理由は、System.Web.Services.WebService 属性を持つインターフェイスによって構成されるサービス動作のコントラクトは、さまざまなクラスで再利用可能であり、同じコントラクトを異なる方法で実装できるからです。
WCF サービスは、1 つ以上の WCF エンドポイントを定義することによって提供します。 エンドポイントは、アドレス、バインディング、およびサービス コントラクトによって定義します。 アドレスは、サービスの存在する場所を定義します。 バインディングは、サービスとの通信方法を指定します。 サービス コントラクトは、サービスが実行できる動作を定義します。
通常、最初にサービス コントラクトを定義します。.NET インターフェイスに System.ServiceModel.ServiceContract および System.ServiceModel.OperationContract 属性を追加することによって定義します。
[ServiceContract]
public interface IEcho
{
[OperationContract]
string Echo(string input);
}
System.ServiceModel.ServiceContract 属性は、そのインターフェイスがWCF サービス コントラクトを定義するものであることを指定します。System.ServiceModel.OperationContract 属性は、そのインターフェイスのメソッドのうちサービス コントラクトの動作を定義するメソッド (ある場合) を指定します。
サービス コントラクトを定義した後、そのサービス コントラクトをクラスに実装します。単純に、サービス コントラクトを定義したインターフェイスをクラスで実装します。
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 |
MSMQ による WCF ソフトウェア エンティティ間の通信 |
| MsmqIntegrationBinding |
MSMQ による WCF ソフトウェア エンティティと他のソフトウェア エンティティ間の通信 |
| NetPeerTcpBinding |
Windows Peer-to-Peer Networking による WCF ソフトウェア エンティティ間の通信 |
定義済みのバインディングである System.ServiceModel.BasicHttpBinding には、ASP.NET Web サービスでサポートされるプロトコルの集合が組み込まれています。
WCF アプリケーション用のカスタム バインディングは、WCF が個々のプロトコルを実装するために使用するバインディング要素クラスの集合として、容易に定義できます。 新しいプロトコルを表す、新しいバインディング要素を作成できます。
サービス タイプの内部動作は、ビヘイビア と呼ばれるクラスのファミリーのプロパティを使用して調整できます。 次の例では、System.ServiceModel.ServiceBehavior クラスを使用して、サービス タイプをマルチスレッド化することを指定しています。
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
System.ServiceModel.ServiceBehavior のように、プログラマが設定すると考えられるプロパティを持つ一部のビヘイビアは、属性です。 管理者が設定すると考えられるプロパティを持つその他のビヘイビアは、アプリケーションの構成で変更できます。
サービス タイプのプログラミングでは、System.ServiceModel.OperationContext クラスが頻繁に使用されます。 このクラスの静的な Current プロパティは、動作を実行するコンテキストに関する情報へのアクセスを提供します。 したがって、System.ServiceModel.OperationContext は、System.Web.HttpContext および System.EnterpriseServices.ContextUtil クラスの両方と同様です。
ホスティング
ASP.NET Web サービスは、クラス ライブラリ アセンブリにコンパイルされます。 拡張子 .asmx のサービス ファイルと呼ばれるファイルが提供されます。このファイルには、サービスのコードを含むクラスと、それが存在するアセンブリを識別する @ WebService ディレクティブが入っています。
<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>
サービス ファイルが IIS の ASP.NET アプリケーション ルートにコピーされ、アセンブリがそのアプリケーション ルートの \bin サブディレクトリにコピーされます。 この時点で、アプリケーション ルート内のサービス ファイルの URL を使用してアプリケーションがアクセス可能になります。
.NET Framework 2.0 で提供される HttpListener クラスを使用して、IIS の外部の任意の .NET アプリケーションで ASP.NET Web サービスをホスティングする方法について、Aaron Skonnard が解説しています。 (英語) ただし、Skonnard 自身が指摘していているように、これにはかなりの労力が必要です。 (英語)
WCF サービスは、 IIS 5.1 または 6.0、IIS 7 の一部分として提供される Windows Activation Service (WAS)、および任意の .NET アプリケーションで容易にホスティングすることができます。 IIS 5.1 または 6.0 でサービスをホスティングするには、そのサービスが通信トランスポート プロトコルとして HTTP を使用している必要があります。
IIS 5.1、6.0、または WAS でサービスをホスティングする手順は、次のとおりです。
- サービス タイプをクラス ライブラリ アセンブリにコンパイルします。
- サービス ファイルを作成します。.svc 拡張子を使用し、@ ServiceHost ディレクティブでサービス タイプを識別します。
<%@ServiceHost language="c#" Service="MyService" %>
- サービス ファイルを仮想ディレクトリにコピーし、その仮想ディレクトリの \bin サブディレクトリにアセンブリをコピーします。
- 構成ファイルを仮想ディレクトリにコピーし、名前を Web.config にします。
この時点で、アプリケーション ルート内のサービス ファイルの URL を使用してアプリケーションがアクセス可能になります。
.NET アプリケーションで WCF サービスをホスティングするには、サービス タイプをアプリケーションで参照するクラス ライブラリ アセンブリにコンパイルし、WCF の System.ServiceModel.ServiceHost クラスを使用してそのサービスをホスティングするように、アプリケーションをプログラミングします。 次に、必要とされる簡単なプログラミングの例を示します。
string httpBaseAddress = "http://www.woodgrove.com:8000/";
string tcpBaseAddress = "net.tcp://www.woodgrove.com:8080/";
Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);
Uri[] baseAdresses = new Uri[] {
httpBaseAddressUri,
tcpBaseAddressUri};
using(ServiceHost host = new ServiceHost(
typeof(Service), //"Service" is the name of the service type baseAdresses))
{
host.Open();
[...] //Wait to receive messages
host.Close();
}
この例から、WCF の System.ServiceModel.ServiceHost を構築する際に、トランスポート プロトコルのアドレスをどのように指定するかがわかります。 これらのアドレスをベース アドレスといいます。
WCF サービスのエンドポイントに提供するアドレスは、そのエンドポイントのホストのベース アドレスに相対するアドレスです。 ホストは通信トランスポート プロトコルごとに 1 つのベース アドレスを使用できます。 エンドポイントのアドレスは、ホストのいずれかのベース アドレスでそのエンドポイントの通信トランスポート プロトコルのベース アドレスであるものに、相対します。 前の構成ファイルの例では、エンドポイント用に選択された System.ServiceModel.BasicHttpBinding はトランスポート プロトコルとして HTTP を使用するので、エンドポイント EchoService のアドレスは、ホストの HTTP ベース アドレスに相対します。 前の例のホストの場合、HTTP ベース アドレスは http://www.woodgrove.com:8000/ です。 IIS または WAS でサービスをホスティングする場合、ベース アドレスは、そのサービスのサービス ファイルの URL です。
WCF の ASP.NET 互換モード オプションを使用できるのは、IIS または WAS でホスティングされ、トランスポート プロトコルとして HTTP のみで構成されているサービスに限られます。 このオプションをオンに設定するには、次の 2 つのステップが必要です。
- プログラマが System.ServiceModel.AspNetCompatbilityRequirements 属性をサービス タイプに追加し、ASP.NET 互換モードを許可するか、このモードが必須であることを指定する必要があります。
[System.ServiceModel.AspNetCompatibilityRequirements(
RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
- 管理者が 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,
Systemm.ServiceModel,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</buildProviders>
</compilation>
</compilation>
</system.web>
このオプションを使用すれば、WCF を使用するようにサービスを変更する場合、.asmx サービス ファイルの URL を使用するように構成されているクライアントを変更する手間が省けます。
クライアントの開発
ASP.NET Web サービスのクライアントを生成するには、コマンドライン ツール wsdl.exe を使用し、.asmx ファイルの URL を入力として提供します。 WCF でこれに相当するツールは svcutil.exe です。 このツールは、サービス コントラクトの定義とプロキシ クラスの定義の入ったコード モジュールを生成します。 また、サービスのアドレスおよびバインディングの入った構成ファイルも生成します。
リモート サービスのクライアントのプログラミングでは、一般に非同期パターンに従ってプログラミングすることを推奨します。 wsdl.exe ツールで生成されるコードは常に、同期および非同期パターンの両方に既定で対応します。 svcutil.exe ツールで生成されるコードは、どちらか一方のパターンに対応でき、 既定では同期パターンに対応します。 このツールを /async スイッチ付きで実行すると、非同期パターンに対応するコードが生成されます。
ASP.NET の wsdl.exe ツールで生成されるプロキシ クラス内の既定の名前が、WCF の svcutil.exe ツールで生成されるプロキシ クラス内の名前と一致するという保証はありません。 特に、System.Xml.Serialization.XmlSerializer を使用してシリアル化する必要のあるクラスのプロパティ名は、svcutil.exe ツールで生成されるコードでは既定でサフィックス Property が付けられますが、wsdl.exe ツールの場合、そうではありません。
メッセージの表記
ASP.NET Web サービスによって送受信する SOAP メッセージのヘッダーは、カスタマイズ可能です。 System.Web.Services.Protocols から派生したクラスによってヘッダーの構造を定義し、System.Web.Services.SoapHeader 属性を使用してヘッダーの存在を指定します。
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 メッセージの構造を記述するために、属性 System.ServiceModel.MessageContract、System.ServiceModel.MessageHeader、および System.ServiceModel.MessageBody が提供されています。
[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 ItemMesage
{
[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>
このオプションを使用すると、クライアントまたはサービスのコード内のインフラ プロトコル ヘッダーへの参照を回避することができます。すなわち、エンドポイントの構成方法だけで、メッセージにヘッダーが追加されます。
サービスの記述
ASP.NET Web サービスの .asmx ファイルに関して、クエリ wsdl を使用して HTTP GET 要求を発行すると、ASP.NET はそのサービスを記述する WSDL を生成します。 その WSDL が要求への応答として戻されます。
ASP.NET 2.0 では、サービスが Web Services-Interoperability Organization (WS-I) の Basic Profile 1.1 に準拠しているかどうかを検証し、確かに準拠しているという主張を、サービスの WSDL に挿入することができます。 これは、System.Web.Services.WebServiceBinding 属性の ConformsTo および EmitConformanceClaims パラメータを使用して行います。
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
ConformsTo = WsiProfiles.BasicProfile1_1,
EmitConformanceClaims=true)]
public interface IEcho
ASP.NET がサービスに対して生成する WSDL は、カスタマイズ可能です。 このカスタマイズは、WSDL にアイテムを追加するための System.Web.Services.Description.ServiceDescriptionFormatExtension のサブクラスを作成することによって行います。
IIS 5.1、6.0、または WAS でホスティングされる HTTP エンドポイントに、WCF サービスの .svc ファイルについて、クエリ wsdl による HTTP GET 要求を発行すると、WCF はサービスを記述する WSDL を応答します。 .NET アプリケーションでホスティングされるサービスの HTTP ベース アドレスに、クエリ wsdl による HTTP GET 要求を発行しても、同じ結果になります。
ただし、WCF は、WS-MetadataExchange 要求に対しても、サービスを記述する WSDL を生成し、これを応答します。 一方、ASP.NET Web サービスには、WS-MetadataExchange 要求のサポートは組み込まれていません。
WCF が生成する WSDL は、さまざまにカスタマイズすることができます。 System.ServiceModel.ServiceMetadataBehavior クラスでは WSDL をカスタマイズするための機能がいくつか提供されており、完全な制御を行う System.ServiceModel.Design.IWsdlExporter の実装を開発できます。 WCF では WSDL を生成しない構成も可能であり、特定の URL で静的な WSDL ファイルを使用することができます。
<behaviors>
<serviceBehaviors>
<behavior name="DescriptionBehavior">
<metadataPublishing
enableMetadataExchange="true"
enableGetWsdl="true"
enableHelpPage="true"
metadataLocation=
"http://localhost/DerivativesCalculatorService/Service.wsdl"/>
</behavior>
</serviceBehaviors>
</behaviors>
例外処理
ASP.NET Web サービスでは、未処理の例外は SOAP エラーとしてクライアントに戻されます。 また、System.Web.Services.Protocols.SoapException クラスのインスタンスを明示的にスローし、それによってクライアントに送信する SOAP エラーの内容をより強力に制御することもできます。
WCF サービスでは、例外によって機密情報が偶発的に公開されるのを防ぐために、未処理の例外は SOAP エラーとしてクライアントに戻されません。 デバッグの目的で未処理の例外をクライアントに戻すための構成も提供されています。
WCF のプログラミングでは、クライアントに意図的に SOAP エラーを戻すために、汎用タイプ System.ServiceModel.FaultException<T> のインスタンスをスローすることができます (T はデータ コントラクト)。 また、System.ServiceModel.FaultContract 属性を動作に追加して、その動作で発生する可能性のあるエラーを指定することもできます。
[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 = proxy.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 サービスの実装に使用するクラスは、System.Web.Services.WebService から派生できます。
public class Service : WebService, IEcho
{
public string Echo(string input)
{
return input;
}
}
この場合、クラスは System.Web.Services.WebService ベース クラスの Context プロパティを使用して System.Web.HttpContext オブジェクトをアクセスするようにプログラミングできます。 System.Web.HttpContext オブジェクトの Application プロパティを通じてアプリケーションの状態情報を更新および取得することができ、Session プロパティを通じてセッションの状態情報を更新および取得することができます。
ASP.NET では、System.Web.HttpContext の Session プロパティでアクセスするセッション状態情報が実際に保存される場所を、かなりの程度まで制御できます。 この情報は Cookie、データベース、現在のサーバーのメモリ、または特定のサーバーのメモリに保存することができます。 その選択は、サービスの構成ファイルで行います。
WCF では、状態の管理のために拡張可能オブジェクトが提供されています。 拡張可能オブジェクトとは、System.ServiceModel.IExtensibleObject<T> を実装するオブジェクトです。 最も重要な拡張可能オブジェクトは、System.ServiceModel.ServiceHostBase および System.ServiceModel.InstanceContext です。 前者を使用すると、同じホスト上のすべてのサービス タイプのすべてのインスタンスがアクセス可能な状態を維持することができます。後者を使用すると、1 つのサービス タイプの同じインスタンス内で実行中の任意のコードによってアクセス可能な状態を維持することができます。
次に示すサービス タイプ TradingSystem では、System.ServiceModel.ServiceBehavior 属性を使用して、同じプロキシ インスタンスからのすべてのコールが、そのサービス タイプの同じインスタンスにルーティングされることを指定しています。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TradingSystem: ITradingService
クラス DealData は、1 つのサービス タイプの同じインスタンス内で実行中の任意のコードによってアクセス可能な状態を定義します。
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 では、System.Web.HttpContext クラスの状態情報を実際に保存する場所をかなりの程度まで制御できます。一方、WCF の少なくとも最初のバージョンでは、拡張可能オブジェクトの保存場所を制御することはできません。 このことは、WCF サービスで ASP.NET 互換モードを選択する最大の理由になります。 構成による状態管理が必要な場合は、ASP.NET 互換モードを選択することで、System.Web.HttpContext クラスの機能を ASP.NET とまったく同じように使用することができ、System.Web.HttpContext クラスで管理する状態情報の保存場所も構成することができます。
セキュリティ
ASP.NET Web サービスを保護するためのオプションは、そのほとんどが IIS アプリケーションを保護するためのオプションです。 WCF アプリケーションは、IIS だけでなく任意の .NET 実行可能プログラム内でホスティングされる可能性があるため、WCF アプリケーションを保護するためのオプションは、IIS の機能から独立させる必要がありました。 ただし、ASP.NET 互換モードで動作する WCF サービスでは、ASP.NET Web サービス用に提供されている機能も使用可能です。
セキュリティ: 認証
IIS ではアプリケーションへのアクセスを制御する機能が提供されており、これによって匿名アクセスまたは各種の認証モード (Windows 認証、ダイジェスト認証、基本認証、および .NET パスポート認証) を選択できます。 ASP.NET Web サービスへのアクセスを制御するには、Windows 認証オプションを使用できます。 ただし、WCF アプリケーションを IIS でホスティングする場合、IIS はアプリケーションへの匿名アクセスを許可するように構成し、認証は WCF 自身 (Windows 認証の他、さまざまなオプションをサポートする) で管理できるようにする必要があります。 その他の組み込みオプションとしては、ユーザー名トークン、X.509 証明書、SAML トークン、および InfoCard がありますが、カスタムな認証メカニズムを定義することも可能です。
ASP.NET 2.0 では、System.Web.HttpContext クラスの Profile プロパティを使用して、認証されるユーザーに関する情報を、(特定の種類のストアから情報を読み取る方法を認識している) System.Web.Profile.Provider クラス経由でストアから自動的に取得できます。 この ASP.NET 2.0 のメカニズムは、WCF では ASP.NET 互換モード以外でサポートされませんが、カスタム動作で System.Web.Profile.Provider クラスを使用してプロファイル情報を取得することは可能です。
セキュリティ: 偽装
ASP.NET では、ASP.NET Web サービスが特定のユーザーや、現在の要求で提供されたユーザーのいずれの資格情報でも偽装できるようにするためのアイデンティティ要素が提供されています。 ASP.NET 互換モードで動作する WCF アプリケーションでは、この要素を使用して、偽装を構成することができます。
WCF の構成システムでは、偽装する特定のユーザーを指定するため、独自のアイデンティティ要素が提供されています。 また、WCF クライアントおよびサービスで、独自に偽装を構成することも可能です。 クライアントは、要求を送信する時点で現在のユーザーを偽装するように構成できます。
<behaviors>
<endpointBehaviors>
<behavior name="DerivativesCalculatorClientBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Impersonation"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
サービス動作は、現在の要求で提供されたユーザーのいずれの資格情報でも偽装するように構成できます。
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void Receive(Message input)
セキュリティ: アクセス制御リストによる承認
アクセス制御リスト (ACL) を使用して、.asmx ファイルへのアクセスを規制することができます。 ただし、WCF の .svc ファイルの ACL は、ASP.NET 互換モード以外では無視されます。
セキュリティ: ロール ベースの承認
IIS の Windows 認証オプションを、ASP.NET 構成言語で提供される承認要素と組み合わせて使用することにより、ユーザーが属する Windows グループに基づいて、ASP.NET Web サービスに関するロール ベースの承認を容易に行うことができます。 ASP.NET 2.0 では、より汎用的なロール ベース承認メカニズムであるロール プロバイダが導入されています。
ロール プロバイダはいずれも、ユーザーに割り当てられたロールについて問い合わせる簡素なインターフェイスを実装するクラスですが、各ロール プロバイダは、その情報を別のソースから取得する方法を認識しています。 ASP.NET 2.0 では、Microsoft SQL Server データベースからロール割り当てを取得できるロール プロバイダと、Windows Server 2003 Authorization Manager からロール割り当てを取得できるロール プロバイダが提供されています。
WCF アプリケーションも含めて、.NET アプリケーションでは、ロール プロバイダのメカニズムは、実際には ASP.NET とは無関係に使用できます。 次に示す WCF アプリケーションの構成例では、ASP.NET ロール プロバイダの使用が、System.ServiceModel.ServiceAuthorization 動作によって選択されるオプションであることがわかります。
<system.serviceModel>
<services>
<service name="Service.ResourceAccessServiceType"
behaviorConfiguration="ServiceBehavior">
<endpoint
address="ResourceAccessService"
binding="wsHttpBinding"
contract="Service.IResourceAccessContract"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
セキュリティ: クレーム(要求)ベースの承認
WCF における最も重要な技術革新の 1 つは、保護されたリソースへのアクセスに対する、クレームベースの承認のサポートです。 クレームは、タイプ、権限、および値によって構成されます。 運転免許を例にしてみましょう。 運転免許には保有者に関する一連のクレーム項目があり、そのうち 1 つは生年月日です。 このクレームのタイプは生年月日で、その値は運転者の生年月日です。 このクレームによって保有者に与えられる権限では、保有者がクレームの値を使って何ができるかが指定されています。 運転者の生年月日に関するクレームの場合、その権限とは単に所有することであり、運転者はその生年月日を所有するけれども、たとえば変更することはできません。 クレームベースの承認は、ロール ベースの承認を包含しています。ロールはクレームの 1 タイプに過ぎないからです。
クレームベースの承認は、動作のアクセス要件に対して一連のクレームを比較し、その比較結果に基づいてその動作へのアクセスが許可または拒否されることによって実行されます。 WCF では、System.ServiceModel.Description.ServiceAuthorizationBehavior の ServiceAuthorizationManager プロパティにやはり値を割り当てることにより、クレームベースの承認を実行するために使用するクラスを指定できます。
<behaviors>
<serviceBehaviors>
<behavior name='ServiceBehavior'>
<serviceAuthorization
serviceAuthorizationManagerType='Service.AccessChecker, Service' />
</behavior>
</serviceBehaviors>
</behaviors>
クレームベースの承認を実行するために使用するクラスは、 System.ServiceModel.ServiceAuthorizationManager から派生する必要があります。上書きするメソッドは、AccessCheck() だけです。 WCF はサービスの動作が起動されるたびに、このメソッドを呼び出し、System.ServiceModel.OperationContext オブジェクト (ユーザーに関するクレームが ServiceSecurityContext.AuthorizationContext プロパティに含まれている) を提供します。 このように WCF では、ユーザーが認証用に提供したセキュリティ トークンから、そのユーザーに関するクレームのアセンブリが既に完了しているので、それらのクレームが動作の条件を満たしているかどうかの簡単な評価が残るだけとなります。
あらゆる種類のセキュリティ トークンからクレームが自動的にアセンブルされることは、WCF における重要な技術革新です。これにより、クレームに基づく承認のためのコードが、認証メカニズムから完全に独立するからです。 これとは対照的に、ASP.NET における 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 サービスをローカライズする場合は、サービス タイプを特定のカルチャ専用のアセンブリにコンパイルし、カルチャ専用のアセンブリごとにカルチャ専用のエンドポイントを使用します。
内部アーキテクチャ
ASP.NET Web サービス
ASP.NET の最新の実装では、HTTP 要求は、Windows カーネル内の http.sys と呼ばれる HTTP 実装からの System.Web.HttpWorkerRequest オブジェクトという形式で受信します。 この System.Web.HttpWorkerRequest オブジェクトを受信した System.Web.HttpRuntime オブジェクトは、System.Web.HttpWorkerRequest オブジェクト内のデータから System.Web.HttpContext オブジェクトを作成します。 要求は System.Web.HttpContext オブジェクトに Request プロパティとして組み込まれます。これが System.Web.HttpRequest オブジェクトです。
既定では、.asmx ファイルへの HTTP POST 要求を表す System.Web.HttpRequest オブジェクトは、System.Web.IHttpHandler インターフェイスを実装するオブジェクトに渡されます (Skonnard 2003、2004)。 このオブジェクトは System.Web.Services.Protocols.WebServiceHandlerFactory クラスを使用して作成され、ASP.NET Web サービス要求ハンドラ と呼ばれる場合もあります。 WCF を ASP.NET 互換モードに構成している場合、WCF は、ASP.NET Web サービス要求ハンドラの動作を模倣する System.Web.IHttpHandler の実装を提供します。
ASP.NET Web サービス要求ハンドラは、少なくとも 4 つの動作を実行する必要があります。 第 1 に、HTTP 要求に組み込まれた SOAP メッセージを、開発者によって提供されるいずれかの SOAP E\extensions のインスタンスに渡す必要があります (Meier、Vasireddy、Babbar、および Mackman 2004)。 第 2 に、要求を処理するために、.asmx ファイルで参照されているクラスのどのメソッドを起動するかを決定する必要があります。 第 3 に、要求をシリアル化解除して、そのメソッドがパラメータとして想定するタイプのインスタンスにする必要があります。 第 4 に、メソッドを実際に起動し、要求からシリアル化解除したパラメータを渡す必要があります。
SOAP 拡張機能は、System.Web.Services.Protocols.SoapExtension から派生されるタイプです。 これらを使って要求内の SOAP メッセージをアクセスまたは変更してから、.asmx ファイルで参照されているクラスのいずれかのメソッドにメッセージが渡され、処理されます。 これらを使って応答メッセージにアクセスしたり変更したりすることも可能であり、サーバーだけでなくクライアントにも実装できます。 SOAP 拡張機能の一般的な用途は、メッセージのログです。 また、メッセージの暗号化にも使用されます。 SOAP 拡張機能は、コンピュータに導入されているすべてのサービスに適用することも、個々のサービスに適用することもできます。また、カスタムな System.Web.Services.SoapExtension 属性を使用して、サービスの特定の動作に適用することも可能です。
クラスのどのメソッドを起動して要求を処理するかを決定するために、ASP.NET Web サービス要求ハンドラは、既定で要求内の SOAPAction ヘッダーを使用します。 既定で、要求ハンドラは、SOAPAction の HTTP ヘッダーがサービスの名前空間とそれに続く動作の名前で構成されているものと想定しています。 サービスの既定の名前空間は http://tempuri.org/ であり、動作の既定の名前は、その動作を定義するメソッドの名前です。 したがって、この HTTP 要求の処理では、
POST /asmxservice/service.asmx HTTP/1.1
User-Agent: Mozilla/4.0
Content-Type: text/xml; charset= utf-8
SOAPAction: "http://tempuri.org/Echo"
Host: localhost
Content-Length: 314
Expect: 100-continue
Connection: Keep-Alive
<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Echo xmlns="http://tempuri.org/">
<input>Hello, World
</input>
</Echo>
</soap:Body>
</soap:Envelope>
要求ハンドラは、http://localhost/asmxservice/service.asmx に存在するファイル内の @_WebService ディレクティブで参照されるクラスが、Echo という動作を持つ既定の名前空間があるサービスを定義しているものと見なします。 また既定では、この動作はクラス Echo のメソッドによって実装されているものと想定し、ハンドラはこのメソッドを起動して要求の処理を試みます。 以上の動作はすべてカスタマイズ可能です。
SOAPAction の HTTP ヘッダーの代わりに、SOAP メッセージのボディ要素の完全修飾要素名を使用して、要求ハンドラに起動するメソッドを識別させることも可能です。 前の HTTP 要求の例では、ボディ要素は次のとおりです。
<Echo xmlns="http://tempuri.org/">
[...]
</soap:Body>
この要素の完全修飾要素名は http://tempuri.org/Echo です。 この名前は SOAPAction の HTTP ヘッダーと同じなので、ASP.NET Web サービス要求ハンドラがこの名前を使用してメッセージのルーティング方法をどのように判別するかは明らかです。 要求ハンドラに SOAPAction の HTTP ヘッダーではなく SOAP メッセージのボディ要素の完全修飾名を使用させるには、次のように、サービスを実装するクラスに System.Web.Services.Protocols.SoapDocumentService 属性を割り当て、ルーティング スタイルとして RequestElement を指定します。
[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public class Service : WebService, IEcho
また、System.Web.Services.Protocols.SoapDocumentMethod 属性の RequestElementName パラメータを使用して、SOAP メッセージのボディ要素のローカル名を、メソッド名と違うものにすることもできます。
[WebMethod]
[SoapDocumentMethod(RequestElementName="OtherName")]
string Echo(string input);
この場合、ASP.NET Web サービス要求ハンドラは、SOAP メッセージのボディ要素のローカル名を、メソッド名と直接マッチングするのではなく、メソッドに割り当てられた System.Web.Services.Protocols.SoapDocumentMethod 属性の RequestElementName パラメータで指定される値とマッチングします。
サービスの名前空間は、System.Web.Services.WebService 属性の Namespace パラメータを使用して、既定から変更することができます。
[WebService(Namespace = "http://www.woodgrove.com/2006/01/29/")]
public interface IEcho
この例では、インターフェイスに適用された System.Web.Services.WebService 属性のこのパラメータの値を変更していますが、残念ながらバグのため、クラスではなくインターフェイスにこの属性が適用されている場合は、このパラメータを変更しても実際には何の効果もありません。
動作の名前は、その動作を実装するメソッド名とは異なる名前にすることができます。 それには、System.Web.Services.WebMethod 属性の MessageName パラメータの値を指定します。
[WebMethod(MessageName="OtherName")]
string Echo(string input);
この機能を使用して、ASP.NET Web サービス要求ハンドラで多形態メソッドを判別することができます。
[WebMethod(MessageName="OtherName")]
string Echo(string input);
[WebMethod]
string[] Echo(string[] inputs);
メソッドに System.Web.Services.Protocols.SoapDocumentMethod 属性を追加し、この属性の Action パラメータに次のように値を指定します。
[WebMethod]
[SoapDocumentMethod(Action="urn:echoing:echo")]
string Echo(string input);
このようなメソッドの場合、ASP.NET Web サービス要求ハンドラは、SOAPAction ヘッダーをサービスの名前空間と動作名に分解してターゲット メソッドを識別しようとするのではなく、SOAPAction の HTTP ヘッダーを SoapDocumentMethod 属性の Action パラメータで指定される値に単純にマッチングします。
ASP.NET Web サービス要求ハンドラが .asmx ファイルで参照されているクラスのどのメソッドを起動して要求を処理すべきかを識別した後、その要求をシリアル化解除して、そのメソッドがパラメータとして想定するタイプのインスタンスにする必要があります。 これには、System.Xml.Serialization.XmlSerializer が使用されます。
既定では要求ハンドラは、要求に組み込まれている SOAP メッセージが、WSDL 1.1 仕様でドキュメント スタイルと呼ばれているものに準拠しているものと見なします。 このスタイルのメッセージには、任意のスキーマに基づいて構造化されたボディの要素が含まれ、要求ハンドラは System.Xml.Serialization.XmlSerializer に依存してこれらの要素を .NET タイプにシリアル化解除します。 この動作の場合、
[WebMethod]
PurchaseOrderConfirmationType PlaceOrders(PurchaseOrderType order);
ASP.NET Web サービス要求ハンドラは、次のようなドキュメント スタイルの SOAP メッセージを想定します。
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:s1="urn:Woodgrove:2006:January:29" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://tempuri.org/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<tns:PlaceOrders>
<s1:PurchaseOrder>
<s1:Date>2006-01-31</s1:Date>
<s1:LineItems>
<s1:LineItem>
<s1:ItemNumber>1</s1:ItemNumber>
<s1:Quantity>1</s1:Quantity>
<s1:UnitPrice>50.00</s1:UnitPrice>
</s1:LineItem>
</s1:LineItems>
<s1:Total>50.00</s1:Total>
</s1:PurchaseOrder>
</tns:PlaceOrders>
</soap:Body>
</soap:Envelope>
ここで、各要素の名前は、シリアル化解除した後のタイプの名前を表します。 このメカニズムもカスタマイズできます。
- タイプの名前と SOAP メッセージの構成要素とのマッピングは、属性 System.Xml.XmlElement および System.Xml.XmlAttribute をメソッドのパラメータに適用することによってカスタマイズできます。 System.Xml.XmlElement 属性は、ASP.NET Web サービス要求ハンドラが次のタイプをシリアル化解除すると想定する XML 要素の名前を制御します。
[WebMethod]
PurchaseOrderType PlaceOrders
(
[XmlElement("OrderParameter")]
PurchaseOrderType order
);
属性 System.Xml.XmlAttribute がパラメータに追加されている場合は、要求ハンドラはこのパラメータを XML 要素ではなく XML 属性からシリアル化解除すると想定します。
- ASP.NET Web サービス要求ハンドラの既定の動作 (要求に組み込まれている SOAP メッセージがドキュメント スタイルに準拠していると想定すること) を変更し、メッセージが WSDL 1.1 仕様でいう RPC スタイルに準拠していると想定されるようにすることができます。 それには、サービス全体に対しては SoapRpcService 属性を使用し、個々のメソッドに対しては SoapRpcMethod 属性を使用します。 ただし、これらのオプションを使用することは推奨できません。その理由は、RPC スタイルの使用は WS-I Basic Profile 1.1 に含まれておらず、相互運用性を損なう可能性があるからです。
Windows Communication Foundation
WCF クライアントで使用するサービスのプロキシ クラスは、自分のメソッドに渡されたパラメータを System.ServiceModel.Channels.Message オブジェクトにシリアル化します。 その後、このオブジェクトは一連のチャネル経由で渡されます。チャネルの数および性質は、選択したバインディングによって決まります。 これらのチャネルは通常、チャネルが実装しているプロトコルに応じて、System.ServiceModel.Channels.Message オブジェクトの Headers コレクションに System.ServiceModel.Channels.MessageHeader オブジェクトを追加します。 その最後のチャネルは常にトランスポート チャネルです。 このチャネルはエンコーダを使用して (このエンコーダの性質もバインディングによって決まります)、System.ServiceModel.Channels.Message オブジェクトをバイト ストリームにシリアル化し、これをトランスポート チャネルがサーバーに送信します。 サーバー上のリスナーはバイト ストリームを受信すると、エンコーダを使用してこのバイト ストリームを System.ServiceModel.Channels.Message オブジェクトにシリアル化解除します。 このオブジェクトが一連のチャネル経由で渡されます。これらのチャネルは通常、クライアント上の一連のチャネルに対応しています。 その後、System.ServiceModel.Channels.Message オブジェクトが System.ServiceModel.Dispatcher.DispatchRuntime オブジェクトに渡されます。 System.ServiceModel.Dispatcher.DispatchRuntime オブジェクトは、サービス タイプのどのメソッドを起動するかを判別し、System.ServiceModel.Channels.Message オブジェクトからのデータを、そのメソッドがパラメータとして想定するタイプのインスタンスにシリアル化解除し、そのメソッドを起動します。
サービスのバインディングを通じて、WCF 内部でのデータ伝送に使用されるチャネルの選択と動作をカスタマイズできるだけでなく、カスタム チャネルを追加することも容易にできます。 また、プロキシおよび System.ServiceModel.Dispatcher.DispatchRuntime の動作も、ビヘイビアを通じて調整したり完全にカスタマイズしたりできます。 このように、ASP.NET では Web サービス要求ハンドラを制御するための属性が多数提供されていますが、WCF はそれらと同様の属性の集合を提供するだけでなく、カスタム コードを使用してプロキシおよび System.ServiceModel.Dispatcher.DispatchRuntime を制御するというオプションも提供しています。
サービス タイプのどのメソッドを起動して要求を処理するかを判別する際、System.ServiceModel.Dispatcher.DispatchRuntime は SOAPAction ヘッダーに依存します。 既定で WCF 動作の SOAPAction ヘッダーは、サービスの名前空間とそれに続くサービス コントラクト名、およびそれに続く動作名で構成されます。 サービス コントラクトの既定の名前空間は、http://tempuri.org/ です。 サービス コントラクトの既定の名前は、その定義に使用されているインターフェイスまたはクラスの名前です。動作の既定の名前は、それを実装しているメソッドの名前です。 したがって、このプログラムの場合、
[ServiceContract]
public interface IDerivativesCalculator
{
[OperationContract]
decimal CalculateDerivative(
string[] symbols,
decimal[] parameters,
string[] functions);
}
SOAPAction ヘッダーは http://tempuri.org/IDerivativesCalculator/CalculateDerivative になります。 名前空間およびサービス コントラクト名は、System.ServiceModel.ServiceContract 属性の Namespace および Name パラメータを使用して既定から変更できます。動作名は、System.ServiceModel.OperationContract 属性の Name パラメータを使用して既定から変更できます。
[ServiceContract(Namespace="OtherNamespace",Name="OtherContractName"]
public interface IDerivativesCalculator
{
[OperationContract(Name="OtherOperationName")]
decimal CalculateDerivative(
string[] symbols,
decimal[] parameters,
string[] functions);
}
System.ServiceModel.Message オブジェクトからのデータをシリアル化解除する際、System.ServiceModel.Dispatcher.DispatchRuntime は既定で System.Runtime.Serialization.DataContractFormatter を使用します。 System.ServiceModel.DataContract および System.ServiceModel.DataMember 属性の Namespace および Name パラメータを使用して、XML 要素名をシリアル化解除後のクラスにマッチングさせるメカニズムについては、既に説明したとおりです。
System.ServiceModel.Dispatcher.DispatchRuntime は、次のように System.ServiceModel.XmlSerializerFormat 属性を適用することにより、System.Xml.Serialization.XmlSerializer を使用できます。
[ServiceContract, XmlSerializerFormat]
public interface IEcho
この属性は、サービスの個々の動作にも適用できます。
シリアル化解除に System.Runtime.Serialization.DataContractFormatter を使用する場合は、System.ServiceModel.DataContractFormat 属性を使用して、データがドキュメント スタイルまたは RPC スタイルのどちらにあると想定されるかを制御できます。 この属性はサービス コントラクトに適用することも、サービス コントラクトの個々の動作に適用することもできます。
[ServiceContract]
public interface IItemService
{
[OperationContract]
[DataContractFormat(Style=OperationFormatStyle.Rpc)]
public void DeliverItem(ItemMessage itemMessage);
}
WCF 導入の準備: 将来的な統合の容易化
ASP.NET を現在使用していて今後 WCF を採用することが予測される場合は、新しい ASP.NET Web サービスが WCF アプリケーションと確実に連携できるようにするために、次のガイドラインに従ってください。
全般的な推奨事項
新しいサービスには ASP.NET 2.0 を採用します。 そうすれば、新バージョンの改良された機能や拡張機能をもちろん利用できます。 それだけでなく、同じアプリケーション内で ASP.NET 2.0 コンポーネントを WCF コンポーネントと一緒に使用できる可能性もあります。
プロトコル
ASP.NET 2.0 の新しい機能を使用して、WS-I Basic Profile 1.1 への準拠性を確認してください。
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
ConformsTo = WsiProfiles.BasicProfile1_1,
EmitConformanceClaims=true)]
public interface IEcho
このプロファイルに準拠する ASP.NET Web サービスは一般に相互運用性が高く、WCF の定義済みのバインディング System.ServiceModel.BasicHttpBinding を通じて WCF クライアントと確実に相互運用できます。
サービスの開発
System.Web.Services.Protocols.SoapDocumentService 属性を使用して、SOAPAction の HTTP ヘッダーではなく、SOAP メッセージのボディ要素の完全修飾名に基づいてメソッドにメッセージを配布することは避けてください。 WCF は SOAPAction の HTTP ヘッダーを使用してメソッドにメッセージを配布します。
データの表記
既に説明したように、XML の名前空間が明示的に定義されている場合、System.Xml.Serialization.XmlSerializer が既定で型をシリアル化する XML は、System.RuntimeSerialization.DataContractFormatter が型をシリアル化する XML と意味的に同じです。 したがって、将来的に WCF を導入することを前提に、現在の ASP.NET Web サービスで使用するデータ型を定義するときは、次のようにしてください。
- XML スキーマではなく .NET クラスを使用して型を定義します。
- クラスには System.Serializable 属性および System.Xml.Serialization.XmlRoot 属性のみを追加します。後者はその型の名前空間を明示的に定義するために使用します。 .NET クラスの XML への変換方法を制御する目的で、System.Xml.Serialization 名前空間から他の属性を追加することは避けてください。
このアプローチを使用すると、後の段階で System.Runtime.Serialization.DataContract および System.Runtime.Serialization.DataMember 属性を追加して、.NET クラスをデータ コントラクトにすることができます。これらのクラスを送信のためにシリアル化した XML を大幅に変更する必要はありません。 この場合、ASP.NET Web サービスのメッセージで使用しているものと同じ型を、WCF アプリケーションでデータ コントラクトとして処理することができ、WCF アプリケーションのパフォーマンスの向上をはじめ、さまざまな利点が得られます。
セキュリティ
IIS の認証オプションは使用しないでください。 WCF クライアントは、これらのオプションをサポートしていません。 サービスを保護する必要がある場合は、すぐに WCF を導入すべきです。WCF の方が標準プロトコルに基づく豊富なオプションを提供しているためです。
WCF 導入の準備: 将来的な移行の容易化
新しい ASP.NET アプリケーションを将来的に WCF に容易に移行できるようにするには、既に説明した推奨事項に加えて次の事項にも従ってください。
プロトコル
ASP.NET 2.0 の SOAP 1.2 サポートを無効にします。
<configuration>
<system.web>
<webServices >
<protocols>
<remove name="HttpSoap12"/>
</protocols>
</webServices>
</system.web>
</configuration>
こうすることが望ましい理由は、WCF では SOAP 1.1、SOAP 1.2 など、異なるプロトコルに準拠するメッセージは、プロトコル別に異なるエンドポイントを使用する必要があるからです。 ASP.NET 2.0 Web サービスが SOAP 1.1 と SOAP 1.2 を両方ともサポートするように構成されている場合 (既定の設定)、ASP.NET Web サービスの既存の全クライアントと確実に互換性のある、元のアドレスにある単一の WCF エンドポイントにサービスを移行することはできません。
サービスの開発
- WCF では、インターフェイスまたはクラスに System.ServiceModel.ServiceContract 属性を適用することによってサービス コントラクトを定義できます。 クラスよりもインターフェイスにこの属性を適用することを推奨します。そうすれば、そのコントラクトの定義は、複数のクラスで、さまざまな方法で実装可能になるからです。 ASP.NET 2.0 は、System.Web.Services.WebService 属性をインターフェイスおよびクラスに適用することも可能です。 ただし、既に説明したように、ASP.NET 2.0 に存在する不具合のため、System.Web.Services.WebService 属性の Namespace パラメータは、この属性をクラスではなくインターフェイスに適用した場合は無効です。 一般にサービスの名前空間は System.Web.Services.WebService 属性の Namespace パラメータを使用して既定値 (http://tempuri.org) から変更することが望ましいので、引き続き System.ServiceModel.ServiceContract 属性をインターフェイスまたはクラスに適用して ASP.NET Web サービスを定義する必要があります。
- これらのインターフェイスを定義するメソッドでは、コードをできる限り少なくします。 これらのインターフェイスの処理は、他のクラスに任せるようにします。 そうすれば、新しい WCF サービス タイプを使用しても、インターフェイスの実質的な処理をこれらのクラスに簡単に任せることができます。
- System.Web.Services.WebMethod 属性の MessageName パラメータを使用して、サービスの動作の明示的な名前を指定します。
[WebMethod(MessageName="ExplicitName")]
string Echo(string input);
これが重要である理由は、ASP.NET での動作の既定の名前が、WCF で付けられる既定の名前とは異なるからです。 明示的な名前を指定すれば、既定の名前への依存が避けられます。
- ASP.NET Web サービスの動作を多形態メソッドで実装しないでください。WCF では、多形態メソッドによる動作の実装はサポートされません。
- HTTP 要求をメソッドにルーティングするために使用される、SOAPAction の HTTP ヘッダーの明示的な値を、System.Web.Services.Protocols.SoapDocumentMethod 属性で指定します。
[WebMethod]
[SoapDocumentMethod(RequestElementName="ExplicitAction")]
string Echo(string input);
- この場合も、このアプローチを採用すれば、既定で使用される SOAPAction 値が ASP.NET と WCF で同じであることに依存する必要性を回避できます。
- SOAP 拡張機能は使用しないでください。 SOAP 拡張機能が必要な場合は、その拡張機能を検討する目的の機能が WCF で既に提供されていないかどうかを確認してください。 これに該当する場合は、WCF をすぐには導入しないという判断を再検討してください。
状態の管理
サービスで状態を維持する必要がないようにします。 状態を維持すると、アプリケーションのスケーラビリティが損なわれる傾向があるだけではありません。WCF の ASP.NET 互換モードで ASP.NET のメカニズムがサポートされているとはいえ、ASP.NET と WCF とでは、状態を管理するメカニズムがまったく異なります。
例外処理
サービスが送受信するデータ型の構造を設計する際は、そのサービスで発生しうる、クライアントに伝達する必要が生じうる、各種の例外を表す構造も設計します。
[Serializable]
[XmlRoot(
Namespace="ExplicitNamespace", IsNullable=true)]
public partial class AnticipatedException {
private string anticipatedExceptionInformationField;
public string AnticipatedExceptionInformation {
get {
return this.anticipatedExceptionInformationField;
}
set {
this.anticipatedExceptionInformationField = value;
}
}
}
これらのクラスには、自分自身を XML にシリアル化する機能を与えます。
public XmlNode ToXML()
{
XmlSerializer serializer = new XmlSerializer(
typeof(AnticipatedException));
MemoryStream memoryStream = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(
memoryStream, UnicodeEncoding.UTF8);
serializer.Serialize(writer, this);
XmlDocument document = new XmlDocument();
document.LoadXml(new string(
UnicodeEncoding.UTF8.GetChars(
memoryStream.GetBuffer())).Trim());
return document.DocumentElement;
}
そうすれば、これらのクラスを使用して、明示的にスローされた System.Web.Services.Protocols.SoapException インスタンスについての詳細情報を提供できます。
AnctipatedException exception = new AnticipatedException();
exception.AnticipatedExceptionInformation = "...";
throw new SoapException(
"Fault occurred",
SoapException.ClientFaultCode,
Context.Request.Url.AbsoluteUri,
exception.ToXML());
これらの例外クラスは、WCF の System.ServiceModel.FaultContract<T> で容易に再利用可能です。
throw new FaultException<AnticipatedException>(anticipatedException);
セキュリティ
- ASP.NET 2.0 のプロファイルは使用しないでください。
- サービスへのアクセスを制御する目的で ACL を使用しないでください。
- サービスのリソースへのアクセスを承認するには、ASP.NET 2.0 ロール プロバイダの使用を検討してください。
WCF の導入
ASP.NET Web サービスとの共存
新規の開発作業に WCF を採用しながら、ASP.NET で開発した既存のアプリケーションのメンテナンスを続行することができます。 あらゆるシナリオで .NET アプリケーションとの通信を容易にするために最適な選択肢となるように設計されている WCF は、ソフトウェアの通信に関連する広範囲の問題に ASP.NET では不可能な方法で対応する、まさに標準ツールとしての役割を果たすことができます。
既存の ASP.NET Web サービスと同じコンピュータに、新しい WCF アプリケーションを導入できます。 これらの ASP.NET Web サービスで使用している .NET がバージョン 2.0 未満の場合は、ASP.NET IIS 登録ツールを使用して、新しい WCF アプリケーションをホスティングする IIS アプリケーションに .NET Framework 2.0 を選択的に導入します。 ASP.NET IIS 登録ツール (Aspnet_regiis.exe) に関するドキュメントを参照してください。 このツールには、IIS 6 管理コンソールに組み込まれた使いやすいユーザー インターフェイスがあります。
WCF を使用して、既存の ASP.NET Web サービスに新機能を追加することができます。それには、ASP.NET 互換モードで動作するように構成した WCF サービスを、IIS の既存の ASP.NET Web サービス アプリケーションに追加します。 ASP.NET 互換モードを使用すると、新しい WCF サービスのコードが System.Web.HttpContext クラスを通じて、既存の ASP.NET コードと同じアプリケーション状態情報をアクセスおよび更新できます。 また、同じクラス ライブラリの共有も可能です。
ASP.NET Web サービスおよびクライアントの WCF への移行
WCF クライアントは、ASP.NET Web サービスを使用できます。 System.ServiceModel.BasicHttpBinding を使用して構成した WCF サービスは、ASP.NET Web サービス クライアントによって使用可能です。 ASP.NET Web サービスは WCF アプリケーションと共存させることができ、WCF を使用して既存の ASP.NET Web サービスに機能を追加することさえ可能です。 以上のように WCF と ASP.NET Web サービスはさまざまな方法で併用が可能であることから、ASP.NET Web サービスを WCF に移行しなければならない切実な理由はあまりありません。
移行が必要と考えられる数少ないケースでも、あるテクノロジから別のテクノロジにコードを単純に移行することは、ほとんどの場合、アプローチとして適切でないことを慎重に考慮してください。 新しいテクノロジを採用する目的は、旧来のテクノロジでは満たすことのできない新しい要件を満たすことであり、その場合、新しく拡張された要件に合うように新しいソリューションを設計することが最善策です。 新しい設計では、既存のシステムでの経験や、そのシステムを設計したときから蓄積された見識を活用することができます。 さらに、新しい設計では、ただ単に旧来の設計を新しいプラットフォームに再現するのではなく、新しいテクノロジの機能を十分に活用することを考慮すべきです。 新しい設計の主な要素をプロトタイプ化した段階で、既存のシステムのコードを新しいシステムで再利用する方法は自ずと明らかになるはずです。
ASP.NET Web サービスを WCF に単純に移植することが最善策と思われる数少ないケースのために、移行をどのように進めるかのガイドラインを次に示します。 サービスの移行方法、次にクライアントの移行方法に関するアドバイスを示します。
ASP.NET Web サービスの WCF への移行
- サービスに関する総合的なテスト スイートがあることを確認します。
- サービスの WSDL を生成し、そのサービスの .asmx ファイルと同じフォルダにコピーを保存します。
- .NET 2.0 を使用するように ASP.NET Web サービスをアップグレードします。 それには、最初に IIS のアプリケーションに .NET Framework 2.0 を導入し、次に Visual Studio 2005 を使用してコード変換プロセスを自動化します (詳細は http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/webprojectsVS05.asp?frame=true を参照)。 テスト スイートを実行します。
- System.Web.Services.WebService 属性の Namespace および Name パラメータに明示的な値がまだ指定されていない場合、それを指定します。 System.Web.Services.WebMethod 属性の MessageName パラメータについても、同様にします。 また、要求をメソッドにルーティングするための SOAPAction の HTTP ヘッダーに明示的な値がまだ指定されていない場合は、System.Web.Services.Protocols.SoapDocumentMethod 属性の Action パラメータの既定値を明示的に指定します。
[WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
public class Adder
{
[WebMethod(MessageName = "Add")]
[SoapDocumentMethod(Action = "http://tempuri.org/Add")]
public double Add(SumInput input)
{
double sum = 0.00;
foreach (double inputValue in input.Input)
{
sum += inputValue;
}
return sum;
}
}
- テスト スイートを実行します。
- クラスのメソッドのボディにある実質的なコードを別のクラスに移動し、そのクラスを元のクラスから使用するようにします。
[WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
public class Adder
{
[WebMethod(MessageName = "Add")]
[SoapDocumentMethod(Action = "http://tempuri.org/Add")]
public double Add(SumInput input)
{
return new ActualAdder().Add(input);
}
}
internal class ActualAdder
{
internal double Add(SumInput input)
{
double sum = 0.00;
foreach (double inputValue in input.Input)
{
sum += inputValue;
}
return sum;
}
}
- テスト スイートを実行します。
- ASP.NET Web サービス プロジェクトに、WCF アセンブリ System.ServiceModel および System.Runtime.Serialization の参照を追加します。
- WCF の svcutil.exe ツールを実行して、WSDL から WCF プロキシ クラスを生成します。 生成されたクラス モジュールをソリューションに追加します。
- 前のステップで生成されたクラス モジュールには、インターフェイスの定義が含まれています。
[System.ServiceModel.ServiceContractAttribute()]
public interface AdderSoap
{
[System.ServiceModel.OperationContractAttribute(
Action="http://tempuri.org/Add",
ReplyAction="http://tempuri.org/Add")]
AddResponse Add(AddRequest request);
}
Modify the definition of the ASP.NET Web service class so that the class is defined as implementing that interface:
[WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
public class Adder: AdderSoap
{
[WebMethod(MessageName = "Add")]
[SoapDocumentMethod(Action = "http://tempuri.org/Add")]
public double Add(SumInput input)
{
return new ActualAdder().Add(input);
}
public AddResponse Add(AddRequest request)
{
return new AddResponse(
new AddResponseBody(
this.Add(request.Body.input)));
}
}
- プロジェクトをコンパイルします。 ステップ 9 で生成されたコードで一部の型定義が重複しているために、エラーが発生する可能性があります。 このようなエラーを訂正するには通常、前から存在していた型の定義を削除します。 テスト スイートを実行します。
- System.Web.Services.WebService、System.Web.Services.WebMethod、 System.Web.Services.Protocols.SoapDocumentMethod 属性など、ASP.NET 固有の属性を削除します。
public class Adder: AdderSoap
{
public double Add(SumInput input)
{
return new ActualAdder().Add(input);
}
public AddResponse Add(AddRequest request)
{
return new AddResponse(
new AddResponseBody(
this.Add(request.Body.input)));
}
}
- ASP.NET Web サービスが次のいずれかに依存していた場合は、WCF の ASP.NET 互換モードを使用するようにクラス (すなわち WCF サービス タイプ) を構成します。
- System.Web.Services.HttpContext クラス
- ASP.NET プロファイル
- .asmx ファイルの ACL
- IIS 認証オプション
- ASP.NET 偽装オプション
- ASP.NET グローバリゼーション
[System.ServiceModel.AspNetCompatibilityRequirements(
RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
public class Adder: AdderSoap
- 元の .asmx ファイル名を .asmx.old に変更します。
- サービス用の WCF サービス ファイルを作成し、拡張子 .asmx を付け、IIS のアプリケーション ルートに保存します。
<%@Service Class="MyOrganization.Adder" %>
<%@Assembly Name="MyServiceAssembly" %>
- サービスの WCF 構成を WCF の Web.config ファイルに追加します。 サービスが BasicHttpBinding を使用し、前のステップで作成した拡張子 .asmx 付きのサービス ファイルを使用し、自身で WSDL を生成せず、代わりに上のステップ 2 の WSDL を使用するように構成します。 また、上のステップ 10 で必要と判断された場合は、ASP.NET 互換モードを使用するように構成します。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation>
<buildProviders>
<remove extension=".asmx" />
<add extension=".asmx"
type=
"System.ServiceModel.ServiceBuildProvider, System.ServiceModel, Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
</buildProviders>
</compilation>
</system.web>
<system.serviceModel>
<services>
<service name="MyOrganization.Adder "
behaviorConfiguration="AdderBehavior">
<endpoint
address=""
binding="basicHttpBinding"
contract="AdderSoap "/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="AdderBehavior">
<metadataPublishing
enableMetadataExchange="true"
enableGetWsdl="true"
enableHelpPage="true"
metadataLocation=
"http://MyHost.com/AdderService/Service.wsdl"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled ="true"/>
</system.serviceModel>
</configuration>
- 構成を保存します。
- プロジェクトをコンパイルします。
- テスト スイートを実行します。
ASP.NET Web サービス クライアントの WCF への移行
- クライアントに関する総合的なテスト スイートがあることを確認します。
- Visual Studio 2005 を使用して、クライアント アプリケーションを .NET 2.0 にアップグレードします。 テスト スイートを実行します。
- クライアント プロジェクトから ASP.NET プロキシ コードを削除します。 このコードは通常、wsdl.exe ツールで生成したモジュールに入っています。
- svcutil.exe ツールを使用して WCF プロキシ コードを生成します。 このコードをクライアント プロジェクトに追加し、構成出力をクライアントの既存の構成ファイルに結合します。
- アプリケーションをコンパイルします。 旧来の ASP.NET プロキシ タイプへの参照を、新しい WCF プロキシ タイプへの参照に置き換えて、コンパイル エラーを訂正します。
- テスト スイートを実行します。
まとめ
ASP.NET Web サービスのツールは Web サービスの構築のみを目的としているのに対し、WCF では、ソフトウェア エンティティを相互に通信させる必要のある、あらゆる状況で使用するツールが提供されます。 Web サービス開発プロジェクトにおいても、WCF は ASP.NET Web サービスよりも多くの Web サービス プロトコルをサポートしています。 これらのプロトコルは、信頼性の高いセッションやトランザクションをはじめ、より高度なソリューションを実現します。 ほとんどのケースで推奨される措置は、新しい開発作業には WCF を採用しながら、既存の ASP.NET Web サービス アプリケーションのメンテナンスを続行することです。 そうすれば、WCF の利点を活用すると同時に、既存のアプリケーションを移行するコストを節約できます。 新しい WCF アプリケーションは、既存の ASP.NET Web サービスを使用することができ、既存の ASP.NET アプリケーションと共存可能です。 さらに WCF の ASP.NET 互換モードを利用して、既存の ASP.NET アプリケーションの新しい運用機能を WCF でプログラミングすることも可能です。