2

私はリアクティブプログラミングに不慣れで、問題に遭遇しました...

私のコードは次のようになっています。

    IsBusy = true;
    service.BeginGetClients(param, c => 
    {
        var r = service.EndGetClients(c);
        if(!CheckResult(r))
        {
            service.BeginGetCachedClients(param, c2 =>
                {
                    var r2 = service.EndGetCachedClients(c2);
                    if(CheckResult(r2))
                        ShowMessage("clients valid");
                    else
                        ShowMessage("clients not valid");

                    UpdateClients(r2);

                    service.BeginUpdateClients(r2, c3 =>
                        {
                            var b = service.EndUpdateClients(c3);
                            if(b)
                                ShowMessage("clients updated");
                            else
                                ShowMessage("clients not updated");
                            IsBusy = false;
                        }, null);

                }, null);
        }
        else
        {
            ShowMessage("error on get clients");
            IsBusy = false;
        }
    }, null);

どうすれば流暢なRxに変更できますか?私はこのコードから始めました:

    var invokeClients = Observable.FromAsyncPattern<Param, List<Client>>(service.BeginGetClients, service.EndGetClients);
    var invokeCachedClients = Observable.FromAsyncPattern<Param, List<Client>>(service.BeginGetCachedClients, service.EndGetCachedClients);
    invokeClients(param)
    .Subscribe(r =>
    {
        if(!CheckResult(r))
        {
            invokeCachedClients(param)
            .Subscribe(r2 =>
            {
                // TODO: next op
            });
        }
    });

このコードを改善する提案はありますか?多分別の解決策?このカスケードコードは好きではありません...

ありがとう!

4

3 に答える 3

1

上の回答の通り

invokeClients(param)
  .Where(x => !CheckResult(x))
  .SelectMany(invokeCachedClients)
  .Subscribe(x => Console.WriteLine("Do something here"));

次のように書くこともできます

var query = from client in invokeClients(param)
  where !CheckResult(client)
  from cache in invokeCachedClients(client)
  select cache;

次に、ビジーフラグをリソースにラップすることができます

Func<IDisposable> busyResource = 
  () =>
  {
    IsBusy = true;
    return Disposable.Create(() => IsBusy = false);
  };

これで、Using ファクトリを使用してまとめて使用できます。

Observable.Using(busyResource, _=>query)
  .Subscribe(x=>Console.Write("Do something here");

OnError または OnCompleted に IsBusy セッターを配置する代わりに Using メソッドを好む理由は、サブスクリプションも破棄された場合に停止するためです。

おそらくこれよりもうまくやれると確信していますが、あなたが実際に何をしているのか理解するのが難しいと感じています. 正直に言うと、これは実際には Rx よりも TPL やデータ ワークフローに適していると思います。つまり、一連のイベントに反応するのではなく、実際に作業の継続を連鎖させているのです。

于 2012-02-06T22:31:11.897 に答える
1
invokeClients(param)
    .Where(x => !CheckResult(x))
    .Select(invokeCachedClients)
    .Do(_ => IsBusy = true)
    .Merge()
    .Do(_ => IsBusy = false)
    .Subscribe(x => Console.WriteLine("Do something here"));

そうしないと機能しません (LINQ クエリを Foreach しないのと同じです)。

于 2011-12-21T19:44:52.373 に答える
0

このようなものでは、常に裏返しに行きたいと思っています。これを翻訳する上で最も難しい部分は、非同期呼び出しの間の部分です。ある結果を次の結果にそのままフィードする場合、それは単純なfrom x in async1() from y in async2(x) .... これを次の 2 つの関数に分割します。

public IObserservable<string> UpdateCachedClients(object param)
{
    var getCachedClientsAsync = Observable.FromAsyncPattern<...>(service.BeginGetCachedClients, service.EndGetCachedClients);
    var updateClientsAsync = Observable.FromAsyncPattern<...>(service.BeginUpdateClients, service.EndUpdateClients);

    return Observable.Create<string>(obs =>
    {
        return (from r2 in getCachedClientsAsync(param)
                            .Do(v => 
                                { if (CheckResult(v))
                                      obs.OnNext("clients valid");
                                  else
                                      obs.OnNext("clients not valid");
                                  //huh? is this done twice
                                  UpdateClients(v); 
                                })
                from b in updateClientsAsync(r2)
                select (b ? "clients updated" : "clients not updated")
               ).Subscribe(obs);
    });
}

public IObservable<string> UpdateAllClients(object param)
{
    var getClientsAsync = Observable.FromAsyncPattern<...>(service.BeginGetClients, service.EndGetClients);
    return from r in getClientsAsync(param)
           select (CheckResult(r) ? 
                     Observable.Return("error on get clients") :
                     UpdateCachedClients(param));
}

Observable.Create結果を渡して次の呼び出しを続行する最も簡単な方法と思われるため、最初の関数での追加レイヤーを使用しました。これら 2 つの関数を取得したら、次のように呼び出すことができるはずです。

IsBusy = true;
var disp = UpdateAllClients(param)
            .Subscribe(ShowMessage,
                       ex => IsBusy = false,
                       () => IsBusy = false);

IsBusyどちらも IObservable の終了メッセージであるため、OnError と OnCompleted の両方で false に設定したことに注意してください。

TPL は 1 つの値しか生成しないため、このような非同期メソッドにより自然に適合するように思われますが、async/await を使用する言語の次のバージョンまでは、Tasks を使用すると、おそらくあなたのメソッドまたは私のメソッドと同様の構文になるでしょう。 IObservables の代わりに。

于 2011-12-22T00:36:02.800 に答える