Silverlightのサービスコールは非同期のみサポートとなっています。そのため、Silverlightアプリでサービスコールを記述するのはちょっと大変です。
そこで、Reactive Extensionsを使用して、非同期通信処理を連続して書けるか試してみます。
Reactive Extensionsとは
Reactive Extensionsを説明するのは、正直私の手にはあまりまくりな代物です。下記のサイトなどが参考になると思います。
Reactive Extensionsのインストール
最初にSilverlight用のReactive Extensionsをサイトからダウンロードしインストールしましょう。
プロジェクトの作成
通常のSilvrlightアプリケーションと同様にプロジェクトを作成します。そして、Silvrlightアプリプロジェクトに以下のアセンブリの参照を追加します。
- System.Interactive.dll
- System.Reactive.dll
- System.CoreEx.dll
- System.Observable.dll
テスト用サービスの作成
テスト用に使うWCFサービスを作成します。
[ServiceContract] public interface IService1 { [OperationContract] string GetA(string keyA); [OperationContract] string GetB(string keyB); [OperationContract] string GetC(string keyC); } public class Service1 : IService1 { public string GetA(string keyA) { System.Diagnostics.Debug.WriteLine("Server.GetA"); return keyA+" : ValueA"; } public string GetB(string keyB) { // 重い処理を実行 Thread.Sleep(5000); System.Diagnostics.Debug.WriteLine("Server.GetB"); return keyB + " : ValueB"; } public string GetC(string keyC) { // 重い処理を実行 Thread.Sleep(5000); System.Diagnostics.Debug.WriteLine("Server.GetC"); return keyC + " : ValueC"; } }
引数で受け取った文字列に文字列を連結して返すだけの単純なサービスを3つ用意しました。
サービスコールを1つ呼ぶ
では、実際にSilvrlightアプリからReactive Extensionsを使用してサービスコールをします。最初は、サービスを1つのみコールします。以下のサイトを参考にしました。
// サービスクライアントの生成 Service1Client client = new Service1Client(); // サービスコンプリートイベント監視用Observableインスタンスを定義 IObservable<IEvent<GetACompletedEventArgs>> getACompleted = Observable.FromEvent<GetACompletedEventArgs>(client, "GetACompleted"); // サービスコンプリートをキャッチし、処理を行う。 getACompleted.Subscribe((IEvent<GetACompletedEventArgs> e) => { System.Diagnostics.Debug.WriteLine("サービスコール完了 = " + e.EventArgs.Result); }); client.GetAAsync("test");
通常のサービスコールとほとんど変わらないコードになります。ポイントは、Observable.FromEventメソッドで作ったIObservable型のインスタンスのSubscribeメソッドを介してサービスからの戻り値を受け取るところです。
上記のコードを実行すると、出力結果はこうなります。
Server.GetA サービスコール完了 = test : ValueA
サービスコールを複数連続で呼ぶ
それでは、複数のサービスコールをチェーン状にしたコードを書いてみます。チェーン上にするために、SubscribeメソッドではなくSelectManyメソッドを使用します。SelectManyメソッドを使うと、Subscribeメソッドのようにサービスコンプリートをキャッチでき、しかも戻り値で他のObservableインスタンスを指定することができます。
// サービスクライアントの生成 Service1Client client = new Service1Client(); // サービスコンプリートイベント監視用Observableインスタンスを定義 IObservable<IEvent<GetACompletedEventArgs>> getACompleted = Observable.FromEvent<GetACompletedEventArgs>(client, "GetACompleted"); IObservable<IEvent<GetBCompletedEventArgs>> getBCompleted = Observable.FromEvent<GetBCompletedEventArgs>(client, "GetBCompleted"); IObservable<IEvent<GetCCompletedEventArgs>> getCCompleted = Observable.FromEvent<GetCCompletedEventArgs>(client, "GetCCompleted"); // サービスコールチェーンを定義 getACompleted.SelectMany((IEvent<GetACompletedEventArgs> e) => { System.Diagnostics.Debug.WriteLine("Client1. call GetBAsync"); string keyB = e.EventArgs.Result; client.GetBAsync(keyB); System.Diagnostics.Debug.WriteLine("Client2. return getBCompleted"); return getBCompleted; }) .SelectMany((IEvent<GetBCompletedEventArgs> e) => { // getBCompletedで監視していたイベントの戻り値をSselectManyで取得できる。 System.Diagnostics.Debug.WriteLine("Client3. call GetCAsync"); string keyC = e.EventArgs.Result; client.GetCAsync(keyC); System.Diagnostics.Debug.WriteLine("Client4. return getCCompleted"); return getCCompleted; }) .Subscribe((IEvent<GetCCompletedEventArgs> e) => { string result = e.EventArgs.Result; System.Diagnostics.Debug.WriteLine("Client5. 非同期サービスコールチェーン終了 = " + result); }); // サービスコールチェーンを開始する。 client.GetAAsync("さーびすこーるちぇーん開始");
実行結果を見てみると、SelectManyまたはSubscribeメソッドに渡した関数に値がプッシュされるのは、サービスから戻り値が帰ってきてからになっています。ということで、非同期サービスコールのチェーンは順番通りに動いているようです。
Server.GetA Client1. call GetBAsync Client2. return getBCompleted Server.GetB Client3. call GetCAsync Client4. return getCCompleted Server.GetC Client5. 非同期サービスコールチェーン終了 = さーびすこーるちぇーん開始 : ValueA : ValueB : ValueC
課題
一応、非同期サービスコールを連続して呼ぶことができました。しかし今回の方法は課題があります。
まとめ
Reactive Extensionsを使用して、非同期通信処理を連続して書けるかを試してみました。結果一応書けましたが、課題が残ってしまいました。良い解決方法が見つかったら、またチャレンジしてみたいです。