カスタム サービス ホスト

このサンプルでは、ServiceHost クラスから派生したカスタムのサービス ホストを使用して、サービスの実行時動作を変更する方法を示します。この方法は、多数のサービスを共通方式で構成するという方法の代わりに使用でき、再利用可能です。このサンプルでは、ServiceHostFactory クラスを使用して、カスタムの ServiceHost を、インターネット インフォメーション サービス (IIS) または Windows プロセス アクティブ化サービス (WAS) でホストされる環境で使用する方法も示します。

Aa395224.Important(ja-jp,VS.100).gif 注 :
サンプルは、既にコンピューターにインストールされている場合があります。続行する前に、次の (既定の) ディレクトリを確認してください。

<InstallDrive>:\WF_WCF_Samples

このディレクトリが存在しない場合は、「.NET Framework 4 向けの Windows Communication Foundation (WCF) および Windows Workflow Foundation (WF) のサンプル」にアクセスして、Windows Communication Foundation (WCF) および WF のサンプルをすべてダウンロードしてください。このサンプルは、次のディレクトリに格納されます。

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Hosting\CustomServiceHost

シナリオについて

サービスのメタデータには機密情報が含まれる可能性がありますが、意図的ではない開示を回避するために、Windows Communication Foundation (WCF) サービスの既定の構成では、メタデータは公開されないように設定されています。この動作は、既定の設定ではセキュリティで保護されますが、同時に、サービスの構成の中でメタデータ公開の動作が明示的に有効化されない限り、サービスの呼び出しに必要なクライアント コードをメタデータ インポート ツール (Svcutil.exe など) を使用して生成できないことも意味します。

多数のサービスのメタデータ公開を有効にすると、同一の構成要素が各サービスに追加されます。この結果、本質的に同じ構成情報が大量に発生することになります。各サービスを個別に構成する代わりに、メタデータ公開を有効化する命令型コードを一度だけプログラミングして、そのコードを複数のサービスで再利用するという方法もあります。この方法を使用するには、ServiceHost から派生する新しいクラスを作成し、ApplyConfiguration() メソッドをオーバーライドして命令型コードでメタデータ公開動作を追加します。

Aa395224.Important(ja-jp,VS.100).gif 注 :
このサンプルでは、わかりやすくするために、セキュリティで保護されていないメタデータ公開エンドポイントを作成する方法を示します。このようなエンドポイントを利用するコンシューマーは、匿名で、認証を受けていない可能性もあります。したがって、エンドポイントを配置する前には注意を払い、サービスのメタデータをパブリックに開示することが適切であることを確認する必要があります。

カスタム ServiceHost の実装

ServiceHost クラスは、便利な仮想メソッドを多数公開しています。継承側でこの仮想メソッドをオーバーライドして、サービスの実行時動作を変更することができます。たとえば、ApplyConfiguration() メソッドはサービスの構成情報を構成ストアから読み取り、それに応じてホストの ServiceDescription を変更します。既定の実装は構成をアプリケーションの構成ファイルから読み取りますが、カスタム実装では、ApplyConfiguration() をオーバーライドすれば、命令型コードを使用して ServiceDescription にさらに変更を加えることや、既定の構成ストア全体を置き換えることも可能です (たとえば、サービスのエンドポイント構成を、アプリケーションの構成ファイルではなくデータベースから読み取るようにする場合)。

このサンプルでは、ServiceMetadataBehavior (メタデータ公開を有効にする動作) がサービスの構成ファイルに明示的に追加されていない場合でもこの動作を追加する、カスタムの ServiceHost を構築します。これを実現するには、ServiceHost から継承する新しいクラスを作成して ApplyConfiguration() をオーバーライドします。

class SelfDescribingServiceHost : ServiceHost
{
    public SelfDescribingServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses) { }

    //Overriding ApplyConfiguration() allows us to 
    //alter the ServiceDescription prior to opening
    //the service host. 
    protected override void ApplyConfiguration()
    {
        //First, we call base.ApplyConfiguration()
        //to read any configuration that was provided for
        //the service we're hosting. After this call,
        //this.Description describes the service
        //as it was configured.
        base.ApplyConfiguration();     

        //(rest of implementation elided for clarity)
    }
}

アプリケーションの構成ファイルで指定されている構成が無視されないようにしたいので、ApplyConfiguration() のオーバーライドで最初に行うことは、基本実装の呼び出しです。このメソッドが完了した後で、次の命令型コードを使用して、サービス記述に ServiceMetadataBehavior を追加します。

ServiceMetadataBehavior mexBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (mexBehavior == null)
{
    mexBehavior = new ServiceMetadataBehavior();
    this.Description.Behaviors.Add(mexBehavior);
}
else
{
    //Metadata behavior has already been configured, 
    //so we don't have any work to do.
    return;
}

ApplyConfiguration() のオーバーライドでの最後の処理は、既定のメタデータ エンドポイントの追加です。通常は、サービス ホストの BaseAddresses コレクション内の各 URI に対して 1 つずつメタデータ エンドポイントを作成します。

//Add a metadata endpoint at each base address
//using the "/mex" addressing convention
foreach (Uri baseAddress in this.BaseAddresses)
{
    if (baseAddress.Scheme == Uri.UriSchemeHttp)
    {
        mexBehavior.HttpGetEnabled = true;
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexHttpBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeHttps)
    {
        mexBehavior.HttpsGetEnabled = true;
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexHttpsBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeNetPipe)
    {
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexNamedPipeBinding(),
                                "mex");
    }
    else if (baseAddress.Scheme == Uri.UriSchemeNetTcp)
    {
        this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                MetadataExchangeBindings.CreateMexTcpBinding(),
                                "mex");
    }
}

自己ホストでのカスタム ServiceHost の使用

カスタム ServiceHost の実装が完成したので、これを使用して任意のサービスにメタデータ公開動作を追加できるようになりました。追加するには、SelfDescribingServiceHost のインスタンス内でサービスをホストします。カスタム ServiceHost を自己ホストのシナリオで使用する方法を次のコードに示します。

    SelfDescribingServiceHost host = 
             new SelfDescribingServiceHost( typeof( Calculator ) );
    host.Open();

既定の ServiceHost クラスを使用してサービスをホストする場合と同様に、このカスタム ServiceHost も、サービスのエンドポイント構成をアプリケーションの構成ファイルから読み取ります。ただし、カスタム ホストの内部でメタデータ公開を有効にするというロジックを追加したので、構成の中でメタデータ公開動作を明示的に有効化することは不要になりました。この方法のメリットがはっきりと現れるのは、開発するアプリケーションに複数のサービスがあり、同じ構成要素を繰り返し記述することなく各サービスでメタデータ公開を有効化できるようにする場合です。

IIS または WAS でのカスタム ServiceHost の使用

カスタム サービス ホストを自己ホストのシナリオで使用することは簡単です。サービス ホストのインスタンスを作成して開くことは、アプリケーションのコードで行うからです。ただし、IIS または WAS がホストする環境では、WCF のインフラストラクチャが、受信メッセージに応答してサービスのホストを動的にインスタンス化します。このホスト環境でもカスタム サービス ホストを使用できますが、ServiceHostFactory の形式でコードを追加する必要があります。次に示すコードは、カスタム SelfDescribingServiceHost のインスタンスを返す、ServiceHostFactory の派生クラスの例です。

public class SelfDescribingServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, 
     Uri[] baseAddresses)
    {
        //All the custom factory does is return a new instance
        //of our custom host class. The bulk of the custom logic should
        //live in the custom host (as opposed to the factory) 
        //for maximum
        //reuse value outside of the IIS/WAS hosting environment.
        return new SelfDescribingServiceHost(serviceType,   
                                             baseAddresses);
    }
}

上のコードでわかるように、カスタム ServiceHostFactory の実装は非常に簡単です。すべてのカスタム ロジックは ServiceHost 実装内部にあり、このファクトリによって派生クラスのインスタンスが返されます。

サービス実装と共にカスタム ファクトリを使用するには、いくつかのメタデータをサービスの .svc ファイルに追加する必要があります。

<%@ServiceHost Service="Microsoft.ServiceModel.Samples.CalculatorService"
               Factory="Microsoft.ServiceModel.Samples.SelfDescribingServiceHostFactory"
               language=c# Debug="true" %>

ここでは、Factory 属性を @ServiceHost ディレクティブに追加し、カスタム ファクトリの CLR 型の名前を属性値として渡しました。IIS または WAS がこのサービスのメッセージを受信すると、WCF ホスト インフラストラクチャによって最初に ServiceHostFactory のインスタンスが作成され、次に ServiceHostFactory.CreateServiceHost() が呼び出されてサービス ホスト自体のインスタンスが作成されます。

サンプルの実行

このサンプルには完全な機能を持つクライアントとサービスの実装が用意されていますが、ここでの目的は、カスタム ホストを使用してサービスのランタイム動作を変更する方法を示すことです。次の手順を実行します。

カスタム ホストの影響を確認するには

  1. サービスの Web.config ファイルを開き、その構成ではサービスのメタデータが明示的には有効にされていないことを確認します。

  2. サービスの .svc ファイルを開き、@ServiceHost ディレクティブに Factory 属性があることと、カスタム ServiceHostFactory の名前が指定されていることを確認します。

サンプルをセットアップ、ビルド、および実行するには

  1. Windows Communication Foundation サンプルの 1 回限りのセットアップの手順」が実行済みであることを確認します。

  2. ソリューションをビルドするには、「Windows Communication Foundation サンプルのビルド」の手順に従います。

  3. ソリューションがビルドされたら、Setup.bat を実行し、IIS 7.0 で ServiceModelSamples アプリケーションを設定します。ServiceModelSamples ディレクトリは、IIS 7.0 アプリケーションとして表示されます。

  4. 単一コンピューター構成か複数コンピューター構成かに応じて、「Running the Windows Communication Foundation Samples」の手順に従います。

  5. IIS 7.0 アプリケーションを削除するには、Cleanup.bat を実行します。

参照

処理手順

方法 : IIS で WCF サービスをホストする