C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

2007/03/30 16:28
【 ホスティング 】
[ ホスティングの種類 ]
WCF のホスティングは、大別して以下の3種類があります。
  • セルフ ホスティング
  • Internet Information Services ( IIS ) によるホスティング
  • Windows Process Activation Service ( WAS ) によるホスティング


[ ServiceHost クラス ]
WCF のホスティングを実現するのは ServiceHost クラス (System.ServiceModel) です。
このクラスをインスタンス化し、Open メソッドを呼び出すことで WCF サービスが利用できるようになります。
具体的な手順は以下の通りです。

  1. ServiceHost クラスの コンストラクタ にてサービス クラスの型情報 ( またはサービスクラスのシングルトンオブジェクト ) と0個以上のベースアドレスを指定して、ServiceHost クラスのインスタンスを生成する
  2. ServiceHost オブジェクトの Open メソッド を呼び出してホスティングを開始する
  3. なんらかの方法 ( 例えばコンソールアプリなら Console.ReadLine メソッド ) で待機状態にする
  4. ServiceHost オブジェクトの Close メソッド を呼び出してホスティングを終了する

一つの ServiceHost オブジェクトは、一つのサービス クラスに対応します。一つのサービス クラスに対して複数のエンドポイントを用意することができます。
どういうことかというと、一つのサービス クラスに対して複数のエンドポイントを用意する場合でも、 ServiceHost オブジェクトは一つであるということです。この際、各エンドポイントで異なるプロトコルを使用することも可能です。

セルフ ホスティングの場合、この一連の処理を自分で実装する必要があります。IIS や WAS によるホスティングの場合は、この処理を自分で実装する必要がありません。

なお、ServiceHost クラスは継承可能なので、ServiceHost クラスを継承した独自のクラスを作成して使用することもできます。 ( セルフ ホストでない場合でも可能です。 )


【 WCF ホストの設定 】
[ 設定方法の種類 ]
WCF ホストの設定は、コンフィギュレーションでの設定と、プログラムからの設定がサポートされています。


[ 主な設定項目 ]
WCF ホストの主な設定は以下の通りです。

BaseAddress ( ベースアドレス )
各エンドポイントのアドレスの、ベースとなるアドレスです。

Endpoint ( エンドポイント )
クライアントとの通信に関連する設定で、アドレスとバインディングとコントラクトで構成されます。
エンドポイントは一つのサービス クラスに対して複数用意することができます。また、各エンドポイントで異なるプロトコルを使用することが可能です。

Addresses ( アドレス )
エンドポイントのアドレスです。
アドレスは、完全パス、またはベースアドレスからの相対パスで指定します。
エンドポイントの使用するプロトコルに適合するベースアドレスが存在する場合、アドレスの設定を省略することができます。
アドレスは各エンドポイントで重複しないようにする必要があります。

Binding ( バインディング )
通信に使用するプロトコルや認証方法など、通信方法に関する設定です。
WCF では、標準でいくつかのバインディング・セットが用意されています。バインディング・セットを利用することで、設定が容易になります。バインディング セットは独自にカスタマイズすることが可能です。
また、カスタム バインディング セットを使って、バインディングの設定を本格的に設定することも可能です。

Contract ( コントラクト )
クライアントに公開するサービスのインターフェイスです。
サービス クラス、サービス クラスの基本クラス、サービス クラスが実装するインターフェイスを指定することができます。また、各エンドポイントで異なるコントラクトを設定することも可能です。

Behavior ( ビヘイビア )
クライアントとの通信に関連しない設定です。
ビヘイビアには、 ServiceBehavior ( サービス ビヘイビア ) と EndpointBehavior ( エンドポイント ビヘイビア ) の2つがあります。
サービス ビヘイビアは一つのサービス クラスと対になります。一つのサービス クラスに対して複数のサービス ビヘイビアを適用することはできません。
エンドポイント  ビヘイビアは一つのエンドポイントと対になります。一つのエンドポイントに対して複数のエンドポイント ビヘイビアを適用することはできません。


[ コンフィギュレーションで設定 ]
コンフィギュレーションでのWCF の設定は、 [ system.serviceModel ] セクションにて行います。
特に重要なのが [ system.serviceModel ] - [ services ] - [ service ] セクション です。
このセクションの name 属性にサービス クラスの名称を記述します。サービス クラスの ServiceBehaviorAttribute 属性CongifurationName プロパティでコンフィギュレーション用の名称を指定し、その名称を記述することも可能です。
このセクションは一つのサービス クラスと対になります。一つのサービス クラスに対してこのセクションを複数用意することはできません。
ServiceHost オブジェクは、対応するサービス クラスの設定を行っている service セクションを、コンフィギュレーションから自動で検出してくれます。

ベースアドレス
service セクションの中に [ host ] - [ baseAddresses ] セクションを用意し、その中に [ add ] セクションを用意することで、ベースアドレスを指定することができます。 ( MSDN には baseAddress セクションを用意するように書いてありますが、これは誤りです。 )
 [ add ] セクションは複数用意することが可能です。

エンドポイント
[ service ] セクションの中に [ endpoint ] セクションを用意することでエンドポイントを設定することができます。 [ endpoint ] セクションは複数存在させることが可能です。

アドレス
[ endpoint ] セクションの address 属性にて設定します。

バインディング
[ endpoint ] セクションの binding 属性にバインディング・セットの名称を設定することで、そのエンドポイントに対してバインディング・セットを適用することができます。バインディング・セットをカスタマイズする場合は、 [ system.serviceModel ] - [ bindings ] セクションの中に、カスタマイズするバインディング・セット用のセクションを用意し、更にその中に binding セクションを用意してこれを設定します。そして、 binding セクションの name 属性で指定したバインディング名称を、 endpoint セクションの bindingConfiguration 属性に指定します。

コントラクト
endpoint セクションの contract 属性にサービス コントラクトの名称を記述します。サービス コントラクトの ServiceContractAttribute 属性CongifurationName プロパティ でコンフィギュレーション用の名称を指定し、その名称を記述することも可能です。

ビヘイビア
サービス ビヘイビアは  [ system.serviceModel ] - [ behaviors ] - [ serviceBehaviors ] - [behavior ] セクションで設定します。 behavior セクションの name 属性で指定した名称を [ system.serviceModel ] - [ services ] - [ service ] セクション の behaviorConfiguration 属性に指定します。
エンドポイント ビヘイビアは  [ system.serviceModel ] - [ behaviors ] - [ endpointBehaviors ] - [behavior ] セクションで設定します。そして、  behavior セクションの name 属性で指定した名称を [ system.serviceModel ] - [ services ] - [ service ] - [ endpoint ] セクション の behaviorConfiguration 属性に指定します。


・サンプル
以下に、コンフィギュレーションの設定例を示します。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <services>
            <service name="YokoKen.WCFServices.SampleService" behaviorConfiguration="SampleServiceBehavior">
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost/WCFServices/SampleService.svc"/>
                        <add baseAddress="net.tcp://localhost:10000/WCFServices/SampleService.svc"/>
                        <add baseAddress="net.pipe://WCFServices/SampleService.svc"/>
                    </baseAddresses>
                </host>
                <endpoint name="SampleServiceHttpEndpoint"
                          address=""
                          binding="basicHttpBinding"
                          bindingNamespace="http://schemas.yokoken.net/WCFServices"
                          contract="YokoKen.WCFServices.ISampleService" />
                <endpoint name="SampleServiceNetTcpEndpoint"
                          address=""
                          binding="netTcpBinding"
                          bindingConfiguration="SampleServiceNetTcpBinding"
                          bindingNamespace="http://schemas.yokoken.net/WCFServices"
                          contract="YokoKen.WCFServices.ITestService" />
                <endpoint name="SampleServiceNetNamedPipeEndpoint"
                          address=""
                          binding="netNamedPipeBinding"
                          bindingConfiguration="SampleServiceNetNamedPipeBinding"
                          bindingNamespace="http://schemas.yokoken.net/WCFServices"
                          contract="YokoKen.WCFServices.ITestService" />
            </service>
        </services>
       
        <bindings>
            <netTcpBinding>
                <binding name="SampleServiceNetTcpBinding" >
                    <security mode="None"/>
                </binding>
            </netTcpBinding>
            <netNamedPipeBinding>
                <binding name="SampleServiceNetNamedPipeBinding">
                    <security mode="None"/>
                </binding>
            </netNamedPipeBinding>
        </bindings>
       
        <behaviors>
            <serviceBehaviors>
                <behavior name="SampleServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>


[ プログラムで設定 ]
プログラムで直接設定する場合、ServiceHost オブジェクトを直接操作して設定を行うことになります。
特に重要なのが ServiceHost オブジェクトの Description プロパティ です。このプロパティの型は ServiceDescription クラス (System.ServiceModel.Description) です。

ベースアドレス
ベースアドレスは、 Uri クラス (System) で表わされます。
ベースアドレスの設定は、ServiceHost オブジェクトのコンストラクタにて行います。
また、 ServiceHost オブジェクトのの BaseAddresses プロパティ にて取得のみ行うことができます。このプロパティの型は、 ReadOnlyCollection ジェネリック クラス (System.Collections.ObjectModel) で、ジェネリック引数 T には、 Uri クラスが指定されています。

エンドポイント
エンドポイントは、ServiceEndpoint クラス (System.ServiceModel.Description) で表わされます。
エンドポイントの設定は、 ServiceDescription オブジェクトの Endpoints プロパティ にて行います。このプロパティの型は ServiceEndpointCollection クラス (System.ServiceModel.Description) です。このServiceEndpointCollection オブジェクトの Add メソッド にて、 ServiceEndpoint オブジェクトを追加します。
また、 ServiceHost オブジェクトの AddServiceEndpoint メソッド でエンドポイントを追加することも可能です。

// 追記 ( 2007/03/31 )
ServiceEndpoint のインスタンスを自分で生成するのではなく、AddServiceEndpoint メソッドを使用することを推奨します。
ServiceEndpoint のインスタンスを自分で生成する場合、 Address プロパティを適切に設定してやる必要があります。この時、ベースアドレスからの相対パスでの指定ができません。絶対パスで指定する必要があります。また、 Address プロパティの設定を忘れた場合 ( つまりnull の場合 ) 、 ServiceHost オブジェクトのOpen メソッドの実行途中で NullReferenceException が発生します。この例外情報から、「 Address プロパティが null であるために例外が発生した」という情報は得られません。
AddServiceEndpoint メソッドを使用した場合、これらの問題は発生しません。メソッドの引数 address には、ベースアドレスからの相対パスを指定することができます。ベースアドレスをそのまま使いたければ string.Empty を渡すだけです。 null を指定すれば、適切な例外をスローしてくれるので、原因がすぐにわかります。


アドレス
アドレスは、 EndpointAddress クラス (System.ServiceModel) で表わされます。
アドレスの設定は、ServiceEndpoint オブジェクトの Address プロパティ にて行います。

バインディング
バインディングは、 Binding クラス (System.ServiceModel.Channels) の派生クラスで表わされます。 Binding クラスの派生クラスは、各バインディング・セットに対応して用意されています。
バインディングの設定は、 ServiceEndpoint オブジェクトの Binding プロパティ にて行います。

コントラクト
コントラクトは、 ContractDescription クラス (System.ServiceModel.Description) で表わされます。このクラスのインスタンス生成には、GetContract メソッド を使用することもできます。
コントラクトの設定は、ServiceEndpoint クラスのコンストラクタや、 ServiceHost オブジェクトの AddServiceEndpoint メソッドの引数で行います。また、 ServiceEndpoint オブジェクトの Contract プロパティ にて取得のみ行うことができます。

ビヘイビア
サービス ビヘイビアは、 IServiceBehavior インターフェイス (System.ServiceModel.Description) を実装したクラスで表わされます。
サービス ビヘイビアの設定は、  ServiceDescription オブジェクトの Behaviors プロパティ で行います。このプロパティの型は KeyedByTypeCollection ジェネリック クラス (System.Collections.Generic) で、ジェネリック引数 TItem には IServiceBehavior インターフェイスが指定されています。このKeyedByTypeCollection オブジェクトの Add メソッド にて、 IServiceBehavior オブジェクトを追加します。

エンドポイント ビヘイビアは、 IEndpointBehavior インターフェイス (System.ServiceModel.Description) を実装したクラスで表わされます。
エンドポイント ビヘイビアの設定は、 ServiceEndpoint オブジェクトの Behaviors プロパティ で行います。このプロパティの型は KeyedByTypeCollection ジェネリック クラスで、ジェネリック引数 TItemには IEndpointBehavior インターフェイスが指定されています。このKeyedByTypeCollection オブジェクトの Add メソッドにて、 IEndpointBehavior オブジェクトを追加します。


・サンプル
以下に、プログラムでの設定例を示します。
( 2007/03/31 : AddServiceEndpoint メソッドを使うように修正 )

// using System.ServiceModel;
// using System.ServiceModel.Channels;
// using System.ServiceModel.Description;

private static void SettingSampleServiceHost(ServiceHost sampleServiceHost)
{
    const string sampleServiceBindingNamespace = "http://schemas.yokoken.net/WCFServices";
    Type sampleServiceContractType = typeof(ISampleService);

    Binding sampleServiceHttpBinding = new BasicHttpBinding();
    sampleServiceHttpBinding.Namespace = sampleServiceBindingNamespace;

    Binding sampleServiceNetTcpBinding = new NetTcpBinding(SecurityMode.None);
    sampleServiceNetTcpBinding.Namespace = sampleServiceBindingNamespace;

    Binding sampleServiceNetNamedPipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    sampleServiceNetNamedPipeBinding.Namespace = sampleServiceBindingNamespace;

    ServiceMetadataBehavior sampleServiceMetadataBehavior = new ServiceMetadataBehavior();
    sampleServiceMetadataBehavior.HttpGetEnabled = true;

    sampleServiceHost.AddServiceEndpoint(sampleServiceContractType, sampleServiceHttpBinding, string.Empty);
    sampleServiceHost.AddServiceEndpoint(sampleServiceContractType, sampleServiceNetTcpBinding, string.Empty);
    sampleServiceHost.AddServiceEndpoint(sampleServiceContractType, sampleServiceNetNamedPipeBinding, string.Empty);
    sampleServiceHost.Description.Behaviors.Add(sampleServiceMetadataBehavior);
}


【 セルフ ホスティング 】
冒頭でも説明したように、セルフ ホスティングは ServiceHost オブジェクトの操作を自分で実装します。

[ コンフィギュレーションで設定 ]
ServiceHost オブジェクトは、コンフィギュレーションでの設定を自動で認識します。
以下に、コンフィギュレーションで設定を行う場合のセルフ ホスト プログラムの例を示します。

// using System.ServiceModel;
// using System.ServiceModel.Dispatcher;

static void Main(string[] args)
{
    Type sampleServiceType = typeof(SampleService);
    using (ServiceHost sampleServiceHost = new ServiceHost(sampleServiceType))
    {
        sampleServiceHost.Open();
        foreach (ChannelDispatcher dispatcher in sampleServiceHost.ChannelDispatchers)
        {
            Console.WriteLine("Listening uri: {0}", dispatcher.Listener.Uri.ToString());
        }
        Console.WriteLine("Enter キーを押すと終了します。");
        Console.ReadLine();
        sampleServiceHost.Close();
    }
}


[ プログラムで設定 ]
プログラムで設定する場合は、 ServiceHost オブジェクトに対して設定を行います。
先ほどプログラムでの設定例として挙げた SettingSampleServiceHost メソッドを使って設定を行う、セルフ ホスト プログラム の例を以下に示します。

// using System.ServiceModel;
// using System.ServiceModel.Dispatcher;

static void Main(string[] args)
{
    Type sampleServiceType = typeof(SampleService);
    Uri[] sampleServiceBaseAddress =
        new Uri[]
        {
            new Uri("http://localhost/WCFServices/SampleService.svc"),
            new Uri("net.tcp://localhost:10000/WCFServices/SampleService.svc"),
            new Uri("net.pipe://WCFServices/SampleService.svc")
        };

    using (ServiceHost sampleServiceHost = new ServiceHost(sampleServiceType, sampleServiceBaseAddress))
    {
        SettingSampleServiceHost(sampleServiceHost);

        sampleServiceHost.Open();
        foreach (ChannelDispatcher dispatcher in sampleServiceHost.ChannelDispatchers)
        {
            Console.WriteLine("Listening uri: {0}", dispatcher.Listener.Uri.ToString());
        }
        Console.WriteLine("Enter キーを押すと終了します。");
        Console.ReadLine();
        sampleServiceHost.Close();
    }
}


【 IIS によるホスティング 】
IIS によるホスティングは簡単で、 ServiceHost ディレクティブを記述したsvc ファイルを用意し、コンフィギュレーションまたはプログラムにて WCF の設定を適切に行い、IIS にて仮想フォルダに配置するだけです。
IIS ホストの場合、HTTPプロトコル以外を使用するエンドポイントは使用できません。また、ベースアドレスの設定は無視され、 svc ファイルのアドレスがベースアドレスとなります。

[ コンフィギュレーションで設定 ]
Web.config にて設定を行います。特別な設定はありません。


[ プログラムで設定 ]
・内部動作
IIS によるホスティングの場合、WCF は、HttpApplication クラス (System.Web)PostAuthorizeRequest イベント にハンドラを登録し、そのハンドラの内部で ServiceHostingEnvironment クラス (System.ServiceModel)EnsureServiceAvailable メソッド を呼び出します。 ( 厳密には、このメソッドではなく、EnsureServiceAvailableFast メソッド ( 可視性 internal ) を直接呼び出しています。 )
このメソッドは、内部で ServiceHostFactory クラス (System.ServiceModel.Activation)CreateServiceHost メソッド を使って ServiceHost クラスのインスタンスを生成します。

・設定方法
ServiceHostFactory クラスを継承した独自のクラスを定義し、このクラスの名称を svc ファイルの ServiceHost ディレクティブ の Factory 属性に指定します。すると、IIS は一連の内部動作の流れの中で、このクラスを使用して ServiceHost クラスのインスタンスを生成するようになります。
つまり、ServiceHostFactory クラスを継承した独自のクラスでCreateServiceHost メソッドをオーバーライドすれば、IIS が使用する ServiceHost オブジェクトをカスタマイズすることができます。これにより、コンフィギュレーションを使わずに WCF の設定を行うことができます。

IIS によるホスティングで、プログラムから設定を行う場合の、 svc ファイルの設定例を以下に示します。

<% @ServiceHost Service="YokoKen.WCFServices.SampleService" Factory="YokoKen.WCFServices.Hosts.SampleServiceHostFactory"%>

IIS によるホスティングで、プログラムから設定を行う場合の、 独自 ServiceHostFactory クラスの例を以下に示します。

using System.ServiceModel;
using System.ServiceModel.Activation;

namespace YokoKen.WCFServices.Hosts
{
    public class SampleServiceHostFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            ServiceHost result = base.CreateServiceHost(serviceType, baseAddresses);
            this.SettingSampleServiceHost(result);
            return result;
        }

        private void SettingSampleServiceHost(ServiceHost target)
        {
            // 設定処理
        }
    }
}


【 WAS によるホスティング 】
未調査です。今後の課題ということで。
タグ: .NET C# WCF


WCFの情報が少なく大変参考になりました。

ひとつ気になることがあったので質問します。
typeof(SampleService)
とありましたが、SampleServiceの中身はどう書いたらよかったでしょうか?
WCFを始めたばかりなのでちょっと分かりませんでした。

あと、全体のソースがダウンロードできたらもっと分かりやすかったかも知れません。

2010.02.24 17:13 URL | Sun #- [ 編集 ]












トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/115-a162f055