编程可用和可维护的代码

时间:2011-12-21 14:03:46

标签: .net system.reactive reactive-programming

我是反应式编程的新手,遇到了一个问题......

我的代码看起来像这样:

    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
            });
        }
    });

改进此代码的任何建议?也许另一个解决方 我不喜欢这个级联代码...

谢谢!

3 个答案:

答案 0 :(得分:1)

invokeClients(param)
    .Where(x => !CheckResult(x))
    .Select(invokeCachedClients)
    .Do(_ => IsBusy = true)
    .Merge()
    .Do(_ => IsBusy = false)
    .Subscribe(x => Console.WriteLine("Do something here"));

确保拥有该订阅,否则它将无法工作(它就像不是Foreach的LINQ查询)

答案 1 :(得分: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);
  };

现在您可以使用“使用工厂”来完成它。

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

你赞成使用Using方法而不是将IsBusy setter设置为OnError或OnCompleted的原因是因为如果订阅也被处理掉,它将会停止它。

我相信我们可能做得比这更好,但我发现很难理解你在做什么。老实说,我认为这实际上比Rx更适合TPL或数据工作流,即实际上是在链接工作延续而不是对一系列事件做出反应。

答案 2 :(得分:0)

对于这样的事情,你总是想要进去。翻译这个的最难的部分是异步调用之间的部分。如果你将一个权利的结果输入下一个,那将是一个直接的from x in async1() from y in async2(x) ...。我看到两个函数,我将其分解为:

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层,因为它似乎是传递结果并继续下一次调用的最简单方法。一旦掌握了这两个功能,您就可以将它们称为:

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

请注意,我在OnError和OnCompleted中都将IsBusy设置为false,因为它们是IObservable的终止消息。

TPL似乎更适合像这样的异步方法,因为它们只生成一个值,但是在使用async / await的语言的下一个版本之前,你可能会得到与你的方法或我的方法类似的语法。您使用Tasks而不是IObservables。