次の方法で共有


.NET リモート処理から WCF への移行

この記事では、.NET リモート処理を使用するアプリケーションを移行して Windows Communication Foundation (WCF) を使用する方法について説明します。 これらの製品間で同様の概念を比較し、WCF でいくつかの一般的なリモート処理シナリオを実行する方法について説明します。

.NET リモート処理は、下位互換性のためにのみサポートされているレガシ製品です。 クライアントとサーバーの間で個別の信頼レベルを維持できないため、混合信頼環境間ではセキュリティで保護されません。 たとえば、.NET リモート処理エンドポイントをインターネットや信頼されていないクライアントに公開しないでください。 既存のリモート処理アプリケーションは、より新しく、より安全なテクノロジに移行することをお勧めします。 アプリケーションの設計で HTTP のみを使用し、RESTful である場合は、Web API ASP.NET することをお勧めします。 詳細については、「ASP.NET Web API」を参照してください。 アプリケーションが SOAP に基づいている場合、または TCP などの Http 以外のプロトコルが必要な場合は、WCF をお勧めします。

.NET リモート処理と WCF の比較

このセクションでは、.NET リモート処理の基本的な構成要素と、WCF に相当する構成要素を比較します。 これらの構成要素は、後で WCF で一般的なクライアント/サーバー シナリオを作成するために使用します。 次のグラフは、.NET リモート処理と WCF の主な類似点と相違点をまとめたものです。

.NET リモート処理 WCF(Windows Communication Foundation)
サーバーの種類 亜綱 MarshalByRefObject [ServiceContract]属性でマークする
サービス操作 サーバーの種類のパブリック メソッド [OperationContract]属性でマークする
シリアル化 ISerializable または [Serializable] DataContractSerializer または XmlSerializer
渡されたオブジェクト 値渡しまたは参照渡し 値渡しのみ
エラー/例外 シリアル化可能な例外 FaultContract<TDetail>
クライアント プロキシ オブジェクト 厳密に型指定された透過的なプロキシは、MarshalByRefObjects から自動的に作成されます 厳密に型指定されたプロキシは、ChannelFactory<TChannel を使用してオンデマンドで生成されます>
プラットフォームが必要 クライアントとサーバーの両方で Microsoft OS と .NET を使用する必要があります クロスプラットフォーム
メッセージの形式 非公開 業界標準 (SOAP や WS-*など)

サーバー実装の比較

.NET リモート処理でのサーバーの作成

.NET リモート処理サーバーの種類は MarshalByRefObject から派生し、次のようにクライアントが呼び出すことができるメソッドを定義する必要があります。

public class RemotingServer : MarshalByRefObject  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

このサーバーの種類のパブリック メソッドは、クライアントが使用できるパブリック コントラクトになります。 サーバーのパブリック インターフェイスとその実装の間に分離はありません。1 つの型が両方を処理します。

サーバーの種類が定義されると、次の例のように、クライアントで使用できるようになります。

TcpChannel channel = new TcpChannel(8080);  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingConfiguration.RegisterWellKnownServiceType(  
    typeof(RemotingServer),
    "RemotingServer",
    WellKnownObjectMode.Singleton);  
Console.WriteLine("RemotingServer is running.  Press ENTER to terminate...");  
Console.ReadLine();  

リモート処理の種類をサーバーとして使用できるようにするには、構成ファイルの使用など、さまざまな方法があります。 これはほんの一例です。

WCF でのサーバーの作成

WCF の同等の手順では、パブリック "サービス コントラクト" と具象実装という 2 つの型を作成します。 1 つ目は、[ServiceContract] でマークされたインターフェイスとして宣言されます。 クライアントで使用できるメソッドは、[OperationContract] でマークされます。

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    Customer GetCustomer(int customerId);  
}  

サーバーの実装は、次の例のように、別の具象クラスで定義されています。

public class WCFServer : IWCFServer  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

これらの型が定義されると、次の例のように、WCF サーバーをクライアントで使用できるようになります。

NetTcpBinding binding = new NetTcpBinding();  
Uri baseAddress = new Uri("net.tcp://localhost:8000/wcfserver");  
  
using (ServiceHost serviceHost = new ServiceHost(typeof(WCFServer), baseAddress))  
{  
    serviceHost.AddServiceEndpoint(typeof(IWCFServer), binding, baseAddress);  
    serviceHost.Open();  
  
    Console.WriteLine($"The WCF server is ready at {baseAddress}.");
    Console.WriteLine("Press <ENTER> to terminate service...");  
    Console.WriteLine();  
    Console.ReadLine();  
}  

TCP は、可能な限り類似した状態を維持するために、両方の例で使用されます。 HTTP の使用例については、このトピックで後述するシナリオのチュートリアルを参照してください。

WCF サービスを構成してホストするには、さまざまな方法があります。 これは、"セルフホステッド" と呼ばれる 1 つの例にすぎません。 詳細については、次のトピックを参照してください。

クライアント実装の比較

.NET リモート処理でのクライアントの作成

.NET リモート処理サーバー オブジェクトを使用できるようになったら、次の例のようにクライアントで使用できます。

TcpChannel channel = new TcpChannel();  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingServer server = (RemotingServer)Activator.GetObject(  
                            typeof(RemotingServer),
                            "tcp://localhost:8080/RemotingServer");  
  
RemotingCustomer customer = server.GetCustomer(42);  
Console.WriteLine($"Customer {customer.FirstName} {customer.LastName} received.");

Activator.GetObject() から返される RemotingServer インスタンスは、"透過的なプロキシ" と呼ばれます。クライアントに RemotingServer 型のパブリック API を実装しますが、すべてのメソッドは、別のプロセスまたはコンピューターで実行されているサーバー オブジェクトを呼び出します。

WCF でのクライアントの作成

WCF の同等の手順では、チャネル ファクトリを使用してプロキシを明示的に作成します。 リモート処理と同様に、次の例のように、プロキシ オブジェクトを使用してサーバー上の操作を呼び出すことができます。

NetTcpBinding binding = new NetTcpBinding();  
String url = "net.tcp://localhost:8000/wcfserver";  
EndpointAddress address = new EndpointAddress(url);  
ChannelFactory<IWCFServer> channelFactory =
    new ChannelFactory<IWCFServer>(binding, address);  
IWCFServer server = channelFactory.CreateChannel();  
  
Customer customer = server.GetCustomer(42);  
Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");

この例では、リモート処理の例に最も似ているため、チャネル レベルでのプログラミングを示します。 また、クライアント プログラミングを簡略化するコードを生成する Visual Studio の サービス参照の追加 アプローチも使用できます。 詳細については、次のトピックを参照してください。

シリアル化の使用法

.NET リモート処理と WCF はどちらもシリアル化を使用してクライアントとサーバーの間でオブジェクトを送信しますが、これらの重要な方法は異なります。

  1. さまざまなシリアライザーと規則を使用して、シリアル化する内容を示します。

  2. .NET リモート処理では、"参照渡し" シリアル化がサポートされています。これにより、1 つの層のメソッドまたはプロパティアクセスで、セキュリティ境界を越えて、もう一方の層でコードを実行できます。 この機能はセキュリティの脆弱性を公開し、リモート処理エンドポイントを信頼されていないクライアントに公開しない主な理由の 1 つです。

  3. リモート処理で使用されるシリアル化はオプトアウト (シリアル化しないものを明示的に除外) し、WCF シリアル化はオプトイン (シリアル化するメンバーを明示的にマーク) します。

.NET リモート処理でのシリアル化

.NET リモート処理では、クライアントとサーバーの間でオブジェクトをシリアル化および逆シリアル化する 2 つの方法がサポートされています。

  • 値による – オブジェクトの値は層の境界を越えてシリアル化され、そのオブジェクトの新しいインスタンスが他の層に作成されます。 その新しいインスタンスのメソッドまたはプロパティの呼び出しはローカルでのみ実行され、元のオブジェクトまたは階層には影響しません。

  • 参照により 、特別な "オブジェクト参照" が層の境界を越えてシリアル化されます。 1 つの層がそのオブジェクトのメソッドまたはプロパティと対話すると、元の階層の元のオブジェクトと通信します。 参照オブジェクトは、サーバーからクライアント、またはクライアントからサーバーのどちらかの方向にフローできます。

リモート処理での値渡し型は、次の例のように、[Serializable] 属性でマークされるか、ISerializable を実装します。

[Serializable]  
public class RemotingCustomer  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

参照ごとの型は、次の例のように MarshalByRefObject クラスから派生します。

public class RemotingCustomerReference : MarshalByRefObject  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

リモート処理の参照渡しのオブジェクトによる影響を理解することは、非常に重要です。 いずれかの層 (クライアントまたはサーバー) が参照渡しオブジェクトを他の層に送信する場合、すべてのメソッド呼び出しは、オブジェクトを所有する層で実行されます。 たとえば、サーバーから返される参照によるオブジェクトのメソッドを呼び出すクライアントは、サーバー上でコードを実行します。 同様に、クライアントによって提供される参照渡しオブジェクトのメソッドを呼び出すサーバーは、クライアントでコードを実行します。 このため、.NET リモート処理は、完全に信頼された環境内でのみ使用することをお勧めします。 信頼されていないクライアントにパブリック .NET リモート処理エンドポイントを公開すると、リモート処理サーバーは攻撃に対して脆弱になります。

WCF でのシリアル化

WCF では、値によるシリアル化のみがサポートされます。 クライアントとサーバーの間で交換する型を定義する最も一般的な方法は、次の例のようになります。

[DataContract]  
public class WCFCustomer  
{  
    [DataMember]  
    public string FirstName { get; set; }  
  
    [DataMember]  
    public string LastName { get; set; }  
  
    [DataMember]  
    public int CustomerId { get; set; }  
}  

[DataContract] 属性は、この型を、クライアントとサーバーの間でシリアル化および逆シリアル化できる型として識別します。 [DataMember] 属性は、シリアル化する個々のプロパティまたはフィールドを識別します。

WCF は、階層間でオブジェクトを送信すると、値のみをシリアル化し、もう一方の層にオブジェクトの新しいインスタンスを作成します。 オブジェクトの値とのやり取りはローカルでのみ行われます。.NET リモート処理参照オブジェクトと同様に、他の層とは通信しません。 詳細については、「 シリアル化と逆シリアル化」を参照してください。

例外処理機能

.NET リモート処理の例外

リモート処理サーバーによってスローされる例外は、シリアル化され、クライアントに送信され、他の例外と同様にクライアントでローカルにスローされます。 カスタム例外は、Exception 型をサブクラス化し、[Serializable] でマークすることで作成できます。 ほとんどのフレームワーク例外は既にこの方法でマークされているため、ほとんどの例外はサーバーによってスローされ、シリアル化され、クライアントで再スローされます。 この設計は開発中に便利ですが、サーバー側の情報が誤ってクライアントに開示される可能性があります。 これは、リモート処理を完全に信頼された環境でのみ使用する必要がある多くの理由の 1 つです。

WCF の例外とエラー

WCF では、不注意による情報漏えいにつながる可能性があるため、任意の例外の種類をサーバーからクライアントに返すことを許可しません。 サービス操作によって予期しない例外がスローされた場合、汎用の FaultException がクライアントでスローされます。 この例外では、問題が発生した理由や場所に関する情報は含まれません。一部のアプリケーションではこれで十分です。 より豊富なエラー情報をクライアントに伝える必要があるアプリケーションは、障害コントラクトを定義することによってこれを行います。

これを行うには、最初に [DataContract] 型を作成して、障害情報を伝達します。

[DataContract]  
public class CustomerServiceFault  
{  
    [DataMember]  
    public string ErrorMessage { get; set; }  
  
    [DataMember]  
    public int CustomerId {get;set;}  
}  

サービス操作ごとに使用する障害コントラクトを指定します。

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    [FaultContract(typeof(CustomerServiceFault))]  
    Customer GetCustomer(int customerId);  
}  

サーバーは FaultException を生成してエラー状態を報告します。

throw new FaultException<CustomerServiceFault>(  
    new CustomerServiceFault() {
        CustomerId = customerId,
        ErrorMessage = "Illegal customer Id"
    });  

また、クライアントがサーバーに要求を行うたびに、障害を通常の例外としてキャッチできます。

try  
{  
    Customer customer = server.GetCustomer(-1);  
}  
catch (FaultException<CustomerServiceFault> fault)  
{  
    Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}  

障害コントラクトの詳細については、「 FaultException」を参照してください。

セキュリティに関する考慮事項

.NET リモート処理のセキュリティ

一部の .NET リモート処理チャネルでは、チャネル 層 (IPC と TCP) での認証や暗号化などのセキュリティ機能がサポートされています。 HTTP チャネルは、認証と暗号化の両方にインターネット インフォメーション サービス (IIS) を使用します。 このサポートがあるにもかかわらず、.NET リモート処理は安全ではない通信プロトコルと見なすべきで、完全に信頼された環境内でのみ使用してください。 パブリック リモート処理エンドポイントをインターネットまたは信頼されていないクライアントに公開しないでください。

WCF のセキュリティ

WCF は、.NET リモート処理で見つかった脆弱性の種類に対処するために、セキュリティを念頭に置いて設計されました。 WCF は、トランスポート レベルとメッセージ レベルの両方でセキュリティを提供し、認証、承認、暗号化などの多くのオプションを提供します。 詳細については、次のトピックを参照してください。

WCF への移行

リモート処理から WCF に移行する理由

  • .NET リモート処理は従来の製品です。 .NET リモート処理で説明されているように、これはレガシ製品と見なされ、新しい開発には推奨されません。 WCF または ASP.NET Web API は、新規および既存のアプリケーションに推奨されます。

  • WCF はクロスプラットフォーム標準を使用します。 WCF はクロスプラットフォームの相互運用性を念頭に置いて設計されており、多くの業界標準 (SOAP、WS-Security、WS-Trust など) をサポートしています。 WCF サービスは、Windows 以外のオペレーティング システムで実行されているクライアントと相互運用できます。 リモート処理は主に、サーバー アプリケーションとクライアント アプリケーションの両方が Windows オペレーティング システム上の .NET Framework を使用して実行される環境向けに設計されています。

  • WCF にはセキュリティが組み込まれています。 WCF はセキュリティを念頭に置いて設計されており、認証、トランスポート レベルのセキュリティ、メッセージ レベルのセキュリティなどの多くのオプションを提供しています。リモート処理は、アプリケーションの相互運用を容易にするために設計されましたが、信頼されていない環境ではセキュリティで保護されるようには設計されていませんでした。 WCF は、信頼された環境と信頼されていない環境の両方で動作するように設計されています。

移行に関する推奨事項

.NET リモート処理から WCF に移行するために推奨される手順を次に示します。

  • サービス コントラクトを作成します。 サービス インターフェイスの種類を定義し、[ServiceContract] 属性でマークします。クライアントが [OperationContract] を使用して呼び出しを許可されるすべてのメソッドをマークします。

  • データ コントラクトを作成します。 サーバーとクライアントの間で交換されるデータ型を定義し、[DataContract] 属性でマークします。 クライアントが [DataMember] で使用できるすべてのフィールドとプロパティをマークします。

  • 障害コントラクトを作成します (省略可能)。 エラーが発生したときにサーバーとクライアントの間で交換される型を作成します。 これらの型を [DataContract] と [DataMember] でマークして、シリアル化できるようにします。 [OperationContract] でマークしたすべてのサービス操作については、[FaultContract] でマークして、返される可能性のあるエラーを示します。

  • サービスを構成してホストします。 サービス コントラクトが作成されたら、次の手順では、エンドポイントでサービスを公開するようにバインディングを構成します。 詳細については、「 エンドポイント: アドレス、バインディング、コントラクト」を参照してください。

リモート処理アプリケーションが WCF に移行された後も、.NET リモート処理の依存関係を削除することが重要です。 これにより、リモート処理の脆弱性がアプリケーションから確実に削除されます。 これらの手順は次のとおりです。

  • MarshalByRefObject の使用を中止します。 MarshalByRefObject 型はリモート処理にのみ存在し、WCF では使用されません。 サブクラス MarshalByRefObject を削除または変更するアプリケーションの種類。

  • [Serializable] と ISerializable の使用を中止します。 [Serializable] 属性と ISerializable インターフェイスは、もともと信頼された環境内で型をシリアル化するように設計されており、リモート処理で使用されます。 WCF シリアル化は、[DataContract] と [DataMember] でマークされている型に依存します。 アプリケーションで使用されるデータ型は、[DataContract] を使用するように変更し、ISerializable または [Serializable] を使用しないようにする必要があります。

移行シナリオ

次に、WCF で次の一般的なリモート処理シナリオを実行する方法を見てみましょう。

  1. サーバーからクライアントに値渡しでオブジェクトを返す

  2. サーバーは、クライアントへの参照によってオブジェクトを返します

  3. クライアントがオブジェクトを値別にサーバーに送信する

WCF では、クライアントからサーバーへの参照によるオブジェクトの送信は許可されていません。

これらのシナリオを読むときは、.NET リモート処理のベースライン インターフェイスが次の例のようになります。 ここでは、WCF を使用して同等の機能を実装する方法のみを説明するため、.NET リモート処理の実装は重要ではありません。

public class RemotingServer : MarshalByRefObject  
{  
    // Demonstrates server returning object by-value  
    public Customer GetCustomer(int customerId) {…}  
  
    // Demonstrates server returning object by-reference  
    public CustomerReference GetCustomerReference(int customerId) {…}  
  
    // Demonstrates client passing object to server by-value  
    public bool UpdateCustomer(Customer customer) {…}  
}  

シナリオ 1: サービスが値によってオブジェクトを返す

このシナリオでは、値によってクライアントにオブジェクトを返すサーバーを示します。 WCF は常にサーバーから値によってオブジェクトを返します。そのため、次の手順では、通常の WCF サービスを構築する方法を簡単に説明します。

  1. まず、WCF サービスのパブリック インターフェイスを定義し、[ServiceContract] 属性でマークします。 [OperationContract] を使用して、クライアントが呼び出すサーバー側メソッドを識別します。

    [ServiceContract]  
    public interface ICustomerService  
    {  
        [OperationContract]  
        Customer GetCustomer(int customerId);  
    
        [OperationContract]  
        bool UpdateCustomer(Customer customer);  
    }  
    
  2. 次の手順では、このサービスのデータ コントラクトを作成します。 これを行うには、[DataContract] 属性でマークされた (インターフェイスではなく) クラスを作成します。 クライアントとサーバーの両方に表示する個々のプロパティまたはフィールドは、[DataMember] でマークされます。 派生型を許可する場合は、[KnownType] 属性を使用して識別する必要があります。 WCF でこのサービスのシリアル化または逆シリアル化を許可する型は、サービス インターフェイス内の型とこれらの "既知の型" のみです。 このリストに含まれていない他の型を交換しようとすると、拒否されます。

    [DataContract]  
    [KnownType(typeof(PremiumCustomer))]  
    public class Customer  
    {  
        [DataMember]  
        public string FirstName { get; set; }  
    
        [DataMember]  
        public string LastName { get; set; }  
    
        [DataMember]  
        public int CustomerId { get; set; }  
    }  
    
    [DataContract]  
    public class PremiumCustomer : Customer
    {  
        [DataMember]  
        public int AccountId { get; set; }  
    }  
    
  3. 次に、サービス インターフェイスの実装を提供します。

    public class CustomerService : ICustomerService  
    {  
        public Customer GetCustomer(int customerId)  
        {  
            // read from database  
        }  
    
        public bool UpdateCustomer(Customer customer)  
        {  
            // write to database  
        }  
    }  
    
  4. WCF サービスを実行するには、特定の WCF バインディングを使用して、特定の URL でそのサービス インターフェイスを公開するエンドポイントを宣言する必要があります。 これは通常、サーバー プロジェクトの web.config ファイルに次のセクションを追加することによって行われます。

    <configuration>  
      <system.serviceModel>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    
  5. その後、次のコードを使用して WCF サービスを開始できます。

    ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));  
        customerServiceHost.Open();  
    

    この ServiceHost を起動すると、web.config ファイルを使用して適切なコントラクト、バインディング、エンドポイントが確立されます。 構成ファイルの詳細については、「構成ファイル を使用したサービスの構成」を参照してください。 このスタイルのサーバーの起動は、セルフホスティングと呼ばれます。 WCF サービスをホストするためのその他の選択肢の詳細については、「 ホスティング サービス」を参照してください。

  6. クライアント プロジェクトの app.config は、サービスのエンドポイントに対応するバインディング情報を宣言する必要があります。 Visual Studio でこれを行う最も簡単な方法は、app.config ファイルを自動的に更新する サービス参照の追加を使用することです。 または、これらの同じ変更を手動で追加することもできます。

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    

    サービス参照の追加の使用方法の詳細については、「方法: サービス参照を追加、更新、または削除する」を参照してください

  7. これで、クライアントから WCF サービスを呼び出すことができます。 これを行うには、そのサービスのチャネル ファクトリを作成し、チャネルを要求し、そのチャネルで必要なメソッドを直接呼び出します。 これは、チャネルがサービスのインターフェイスを実装し、基になる要求/応答ロジックを処理するためです。 そのメソッド呼び出しからの戻り値は、サーバーの応答の逆シリアル化されたコピーです。

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    Customer customer = service.GetCustomer(42);  
    Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");
    

WCF によってサーバーからクライアントに返されるオブジェクトは、常に値によって返されます。 オブジェクトは、サーバーによって送信されたデータの逆シリアル化されたコピーです。 クライアントは、コールバックを介してサーバー コードを呼び出す危険なしに、これらのローカル コピーのメソッドを呼び出すことができます。

シナリオ 2: サーバーが参照によってオブジェクトを返す

このシナリオでは、参照によってクライアントにオブジェクトを提供するサーバーを示します。 .NET リモート処理では、これは、参照によってシリアル化される MarshalByRefObject から派生したすべての型に対して自動的に処理されます。 このシナリオの例として、複数のクライアントが独立したセッションフル サーバー側オブジェクトを持つことができます。 前述のように、WCF サービスによって返されるオブジェクトは常に値によって返されるため、参照によるオブジェクトに直接相当するものはありませんが、 EndpointAddress10 オブジェクトを使用して参照セマンティクスに似た何かを実現できます。 これは、クライアントがサーバー上のセッションフルな参照渡しオブジェクトを取得するために使用できる、シリアル化可能な値渡しオブジェクトです。 これにより、独立したセッションフル サーバー側オブジェクトを持つ複数のクライアントを持つシナリオが可能になります。

  1. まず、セッションフル オブジェクト自体に対応する WCF サービス コントラクトを定義する必要があります。

    [ServiceContract(SessionMode = SessionMode.Allowed)]  
        public interface ISessionBoundObject  
        {  
            [OperationContract]  
            string GetCurrentValue();  
    
            [OperationContract]  
            void SetCurrentValue(string value);  
        }  
    

    ヒント

    セッションフル オブジェクトが [ServiceContract] でマークされ、通常の WCF サービス インターフェイスになっていることに注意してください。 SessionMode プロパティを設定すると、セッションフル サービスであることが示されます。 WCF では、セッションは、2 つのエンドポイント間で送信される複数のメッセージを関連付ける方法です。 つまり、クライアントがこのサービスへの接続を取得すると、クライアントとサーバーの間にセッションが確立されます。 クライアントは、この単一セッション内のすべての対話に、サーバー側オブジェクトの 1 つの一意のインスタンスを使用します。

  2. 次に、このサービス インターフェイスの実装を提供する必要があります。 これを [ServiceBehavior] で示し、InstanceContextMode を設定することで、この種類の一意のインスタンスをセッションごとに使用するように WCF に指示します。

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
        public class MySessionBoundObject : ISessionBoundObject  
        {  
            private string _value;  
    
            public string GetCurrentValue()  
            {  
                return _value;  
            }  
    
            public void SetCurrentValue(string val)  
            {  
                _value = val;  
            }  
    
        }  
    
  3. 次に、このセッションフル オブジェクトのインスタンスを取得する方法が必要です。 これを行うには、EndpointAddress10 オブジェクトを返す別の WCF サービス インターフェイスを作成します。 これは、クライアントがセッションフル オブジェクトの作成に使用できるエンドポイントのシリアル化可能な形式です。

    [ServiceContract]  
        public interface ISessionBoundFactory  
        {  
            [OperationContract]  
            EndpointAddress10 GetInstanceAddress();  
        }  
    

    次の WCF サービスを実装します。

    public class SessionBoundFactory : ISessionBoundFactory  
        {  
            public static ChannelFactory<ISessionBoundObject> _factory =
                new ChannelFactory<ISessionBoundObject>("sessionbound");  
    
            public SessionBoundFactory()  
            {  
            }  
    
            public EndpointAddress10 GetInstanceAddress()  
            {  
                IClientChannel channel = (IClientChannel)_factory.CreateChannel();  
                return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress);  
            }  
        }  
    

    この実装では、セッションフル オブジェクトを作成するためのシングルトン チャネル ファクトリが維持されます。 GetInstanceAddress() が呼び出されると、チャネルが作成され、このチャネルに関連付けられているリモート アドレスを効果的に指す EndpointAddress10 オブジェクトが作成されます。 EndpointAddress10 は、クライアントに値で返すことができる単なるデータ型です。

  4. 次の例に示すように、次の 2 つの操作を行って、サーバーの構成ファイルを変更する必要があります。

    1. セッションフル オブジェクトのエンドポイントを説明する <client> セクションを宣言します。 この状況ではサーバーもクライアントとして機能するため、これは必要です。

    2. ファクトリ オブジェクトとセッションフル オブジェクトのエンドポイントを宣言します。 これは、クライアントがサービス エンドポイントと通信して EndpointAddress10 を取得し、セッションフル チャネルを作成できるようにするために必要です。

    <configuration>  
      <system.serviceModel>  
         <client>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
        </client>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
          <service name="Server.MySessionBoundObject">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundObject"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundObject" />  
          </service>  
          <service name="Server.SessionBoundFactory">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundFactory"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundFactory" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    

    次に、次のサービスを開始できます。

    ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
    factoryHost.Open();  
    
    ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject));  
    sessionHost.Open();  
    
  5. クライアントを構成するには、プロジェクトの app.config ファイルでこれらの同じエンドポイントを宣言します。

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
          <endpoint name="factory"  
                    address="net.tcp://localhost:8081/SessionBoundFactory"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundFactory"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    
  6. このセッションフル オブジェクトを作成して使用するには、クライアントで次の手順を実行する必要があります。

    1. ISessionBoundFactory サービスへのチャネルを作成します。

    2. そのチャネルを使用して、そのサービスを呼び出して EndpointAddress10 を取得します。

    3. EndpointAddress10 を使用して、セッションフル オブジェクトを取得するチャネルを作成します。

    4. セッションを持つオブジェクトと対話して、複数の呼び出しにおいて同じインスタンスであることを確認します。

    ChannelFactory<ISessionBoundFactory> channelFactory =
        new ChannelFactory<ISessionBoundFactory>("factory");  
    ISessionBoundFactory sessionFactory = channelFactory.CreateChannel();  
    
    EndpointAddress10 address1 = sessionFactory.GetInstanceAddress();  
    EndpointAddress10 address2 = sessionFactory.GetInstanceAddress();  
    
    ChannelFactory<ISessionBoundObject> sessionObjectFactory1 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address1.ToEndpointAddress());  
    ChannelFactory<ISessionBoundObject> sessionObjectFactory2 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address2.ToEndpointAddress());  
    
    ISessionBoundObject sessionInstance1 = sessionObjectFactory1.CreateChannel();  
    ISessionBoundObject sessionInstance2 = sessionObjectFactory2.CreateChannel();  
    
    sessionInstance1.SetCurrentValue("Hello");  
    sessionInstance2.SetCurrentValue("World");  
    
    if (sessionInstance1.GetCurrentValue() == "Hello" &&  
        sessionInstance2.GetCurrentValue() == "World")  
    {  
        Console.WriteLine("sessionful server object works as expected");  
    }  
    

WCF は常に値によってオブジェクトを返しますが、EndpointAddress10 を使用して参照セマンティクスと同等のセマンティクスをサポートできます。 これにより、クライアントはセッションフルな WCF サービス インスタンスを要求でき、その後は他の WCF サービスと同様に操作できます。

シナリオ 3: クライアントがサーバーに By-Value インスタンスを送信する

このシナリオでは、値によって非プリミティブ オブジェクト インスタンスをサーバーに送信するクライアントを示します。 WCF は値によってのみオブジェクトを送信するため、このシナリオでは通常の WCF の使用方法を示します。

  1. シナリオ 1 と同じ WCF サービスを使用します。

  2. クライアントを使用して新しい値ごとのオブジェクト (Customer) を作成し、ICustomerService サービスと通信するチャネルを作成し、そのオブジェクトをそれに送信します。

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    PremiumCustomer customer = new PremiumCustomer {
    FirstName = "Bob",
    LastName = "Jones",
    CustomerId = 43,
    AccountId = 99};  
    bool success = service.UpdateCustomer(customer);  
    Console.WriteLine($"  Server returned {success}.");
    

    顧客オブジェクトはシリアル化され、サーバーに送信され、そこでそのオブジェクトの新しいコピーに逆シリアル化されます。

    このコードは、派生型 (PremiumCustomer) の送信も示しています。 サービス インターフェイスには Customer オブジェクトが必要ですが、Customer クラスの [KnownType] 属性は PremiumCustomer も許可されたことを示しています。 WCF は、このサービス インターフェイスを介して他の型をシリアル化または逆シリアル化しようとすると失敗します。

データの通常の WCF 交換は値によって行われます。 これにより、これらのデータ オブジェクトの 1 つでメソッドを呼び出すと、ローカルでのみ実行されます。他の層のコードは呼び出されません。 サーバー から 返される参照オブジェクトのようなものを実現することは可能ですが、クライアントが参照オブジェクトをサーバー 渡すことができません。 クライアントとサーバー間の会話を必要とするシナリオは、双方向サービスを使用して WCF で実現できます。 詳細については、「 双方向サービス」を参照してください。

概要

.NET リモート処理は、完全に信頼された環境内でのみ使用することを目的とした通信フレームワークです。 これはレガシ製品であり、下位互換性のためにのみサポートされています。 新しいアプリケーションのビルドには使用しないでください。 逆に、WCF はセキュリティを念頭に置いて設計されており、新規および既存のアプリケーションに推奨されています。 代わりに WCF または ASP.NET Web API を使用するために、既存のリモート処理アプリケーションを移行することをお勧めします。