かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Rx + WCF RIA Services = 簡単?? その2(via 非同期プログラミングはつらいよ(2))

さて、前回書いたRx + WCF RIA Services = 簡単?? via(非同期プログラミングは辛いよ)ですが、コメントで追加の要件がきました!ということでやってみようと思います。
因みにReactive Extensionsを使わない場合の実装例はこちらを見てください。

要件は、こんな感じです。

  • BusyIndicatorのIsBusy=trueに設定する。
  • 顧客リスト取得を開始する。
  • 仕入先リスト取得を開始する。
  • 顧客リストの取得と仕入先リストの取得が完了したら受注リスト取得を開始する。
  • すべて取得が完了したらIsBusy=falseに設定する。

※何れかの処理で例外が発生したらIsBusy=falseに設定したうえで、例外をthrowする。

前のは、例外処理とか何も考えてなかったので敷居が高くなりました。ということで、まず例外処理を意識した形でDomainContextの拡張メソッドを修正します。

public static class DomainContextExtensions
{
    public static IObservable<IEnumerable<TEntity>> LoadAsObservable<TEntity>(this DomainContext self,
        EntityQuery<TEntity> q)
        where TEntity : Entity
    {
        // Loadが終わったら通知するObservableを返す
        var subject = new AsyncSubject<IEnumerable<TEntity>>();
        self.Load(q, op =>
            {
                if (op.HasError)
                {
                    // エラーだったらエラー通知
                    subject.OnError(op.Error);
                    return;
                }
                subject.OnNext(op.Entities);
                subject.OnCompleted();
            }, null);
        return subject.AsObservable();
    }
}

これでエラーも怖くないので、要件を満たすコードを書いていきます。

private EduDomainContext ctx = new EduDomainContext();

public void Load()
{
    this.IsBusy = true;
    // 読み込み開始!
    var cust = this.ctx.LoadAsObservable(this.ctx.GetCustomersQuery(1000));
    var supp = this.ctx.LoadAsObservable(this.ctx.GetSuppliersQuery(2000));
            
    cust.Zip(supp, (c, s) =>
        {
            // 両方読み込み終わったらセット
            this.Customers.Source = c;
            this.Suppliers.Source = s;
            return new Unit();
        })
        .SelectMany(_ =>
        {
            // 続いてOrderを読み込む
            return this.ctx.LoadAsObservable(this.ctx.GetOrdersQuery(3000));
        })
        .Subscribe(
            o =>
            {
                // Orderの取得が出来たのでセット
                this.Orders.Source = o;
            },
            ex =>
            {
                // エラーだったらデータリセットして例外を投げる
                this.Customers.Source = null;
                this.Suppliers.Source = null;
                this.Orders.Source = null;
                this.Message = ex.Message;
                this.IsBusy = false;
                throw new Exception("だめぽ", ex);
            },
            () =>
            {
                // 完了したらIsBusyをfalseに
                this.IsBusy = false;
            });

}

これも割といい感じにかけてるんじゃないんでしょうか。ちょっと気になるのはZipメソッドを使って読み込みの完了をしてるというところですかね・・・。もっといい方法があるという人はコメントください!