ヒント4:バックエンドリモーティングオブジェクトを作成する
次のステップは外部ビジネスオブジェクトの作成です。簡単に言えば、外部クラスは次の3つのことを実行しなければなりません。
ICustomer
を実装します。MarshalByRefObject
という.NETのシステムクラスを継承します。これにより、リモートコードは外部メソッドのドメインにあるクラスを実行できるようになります。- 基本ビジネスオブジェクトを継承します。
ここで問題があります。1つのクラスは1つのクラスと1つのインターフェイスを継承することはできますが、複数のクラスを継承することはできません。この問題を解決するには、MarshalByRefObject
を継承する基本ビジネスオブジェクト(BaseBzObject
)を定義します。これで、BaseBzObject
を継承してICustomer
を実装する実際のビジネスオブジェクト(SimpleCustomerBzObject
)を定義することができます。
これにより、SimpleCustomerBzObject
はBaseBzObject
を通してMarshalByRefObject
を継承するようになります。
BaseBzObject
のコードは次のとおりです。
using System; using System.Collections.Generic; using System.Text; namespace SimpleBaseBzObject { public class BaseBzObject : System.MarshalByRefObject { // Base business object methods go here } }
SimpleCustomerBzObject
のコードは次のとおりです。
using System; using System.Collections.Generic; using System.Text; using System.Data; using SimpleBaseBzObject; using SimpleInterfaces; namespace SimpleCustomerBzObject { public class CustomerBzObject : BaseBzObject, ICustomer { public DataSet GetCustomer(int AccountID) { // do something, return a dataset } } }
ヒント5:クライアントを作成する
インターフェイスとサーバーサイドのビジネスオブジェクトを作成したので、次にこのバックエンドオブジェクトにアクセスするコードを作成する必要があります。
既に説明したように、クライアントには顧客ビジネスオブジェクト(CustomerBzObject
)もその参照もありません。あるのはインターフェイスだけです。お気づきの通り、必要なのはこのインターフェイス(とポート番号とアドレス)だけです。
次のコードは外部クラスにアクセスする方法を示しています。
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using SimpleInterfaces; ICustomer oRemoteCustomer; Type tCustomer = typeof(ICustomer); ChannelServices.RegisterChannel( new TcpClientChannel()); oRemoteCustomer = (ICustomer)Activator.GetObject(tCustomer, "tcp://localhost:8228/CustomerBzObject"); DataSet DsTemp = oRemoteCustomer.GetCustomer(123);
このコードが実行することは次のとおりです。
- System.Runtime.Remoting名前空間への参照を追加します(コードには示されていません)。
- 複数の.NETリモーティング名前空間と、インターフェイスクラスを組み込みます。
ICustomer
インターフェイスへのオブジェクト参照を定義します。最終的にはこのオブジェクト参照を使って、バックエンドメソッドGetCustomer
にアクセスします。ICustomer
への型参照を定義します(サーバーサイドはオブジェクトの型と一致させるためにこれを必要とします)。- TCPチャネルを開きます(この例では、ポート8228とTCPアドレスをハードコーディングしています)。
- リモートオブジェクトをアクティブ化し、戻り値を
ICustomer
にキャストします。この時点で、oRemoteCustomer
はICustomer
で定義されているすべてのプロパティとメソッドにアクセスできるようになります。
最も重要なのは、リフレクションを使わずにこれらを実現できたということです。厳密な型指定によるソリューションを構築することができました。図1に、ICustomer
が実装しているすべてのメソッドがデザイン時にIntelliSenseに表示されることを示します。自分だけでなく開発チームのメンバとも協力して、厳密に型指定された要素をできるだけ多く使うようにしましょう。デザイン時解決とタイプセーフチェックは、特にコラボレーション環境で有益な手法です。
ヒント6:リモーティングサーバーリスナを作成する
もう1つ必要なパーツがあります。ビジネスオブジェクトに関するメソッドコードが置かれているドメインで、リスナを作成する必要があります。このリスナはクライアントコードで指定されたポートをチェックし、リモートアクセス用のビジネスオブジェクトを登録します。
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; TcpServerChannel Tcps; int nTCPPort = 8228; Tcps = new TcpServerChannel(nTCPPort); ChannelServices.RegisterChannel(Tcps); RemotingConfiguration.RegisterWellKnownServiceType( typeof(SimpleCustomerBzObject.CustomerBzObject), "CustomerBzObject", WellKnownObjectMode.Singleton);
このコードは少し難解に見えますが、メソッドの名前は一目瞭然です。ビジネスオブジェクトが置かれているドメインはTCPポートチャネルとCustomerBzObject
を登録します。クライアントサイドのコードを参照すると、オブジェクトの型参照がどのように一致しなければならないかが分かります。
ヒント7:Visual Studio 2005でデータセットリモーティングのパフォーマンスを向上させる
一般的なデータドリブンアプリケーションでは、ある物理的な境界から別の境界へとデータセットを渡します。データセットはオーバーヘッドが大きいため、よく目にするのがデータセットとXML文字列の間で変換を行って転送のオーバーヘッドを削減する手法です。
うれしいことに、Visual Studio 2005では、データセットをバイナリファイルとしてシリアライズすることでパフォーマンスを向上させることができます。
DsCustomer.RemotingFormat = SerializationFormat.Binary;
ヒント8:Windowsサービスを作成する
ヒント7では、リモートコールを待機する基本的なWindowsフォームのアプリケーションのコードを示しました。実際、ほとんどのインストールシステムではリスナがWindowsサービスとして実行されています。
Windowsサービスは、Visual Studio 2003とVisual Studio 2005のどちらでも作成できます。開発者の中には、このプロセスを「簡単すぎて怖いくらいだ」と表現する人もいます。サービスのエンティティ(プロセスインストーラなど)のセットアップについて知る必要はほとんどありませんが、幸い、.NETでWindowsサービスを作成する方法についてはインターネット上で情報がたくさん公開されています。
本稿のサンプルファイルには、Windowsサービス用のプロジェクトが収録されています。
ヒント9:Visual Studio 2005におけるTCPリモーティングの新しいセキュリティ強化
Visual Studio 2003では、IIS下にHTTPリモーティングオブジェクトを置いてセキュリティ手段を確保しなければなりませんでした。Visual Studio 2005では、TCPチャネルが強化され、セキュアなTCP通信を構成することができます。特に、TCPリモーティングは、SSPI(Security Support Provider Interface)を使って暗号化と認証をサポートしています。
クライアントサイドで、必要なセキュリティ設定がすべて含まれた簡単なコレクションを定義し、そのコレクションをTCPチャネルのコンストラクタに渡すことができます。
Dictionary<string, string> oDict = new Dictionary<string, string>(); oDict.Add("secure", "true"); TcpChannel oChannel = new TcpChannel(oDict, null, null); ChannelServices.RegisterChannel(oChannel);
サーバーサイドでは、2つ目のパラメータ(ensureSecurity
)をtrueに設定する必要があります。
Tcps = new TcpServerChannel(nTCPPort);
ChannelServices.RegisterChannel(Tcps,true);