Программирование полезного и обслуживаемого кода - PullRequest
2 голосов
/ 21 декабря 2011

Я новичок в реактивном программировании и столкнулся с проблемой ...

Мой код выглядит так:

    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 ]

1 голос
/ 07 февраля 2012

Согласно ответу выше

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 в OnError или OnCompleted, заключается в том, что это остановит его, если подписка также будет удалена.

Я уверен, что мы могли бы добиться даже большего, чем это, но мне трудно понять, что вы на самом деле делаете. Честно говоря, я думаю, что это на самом деле лучше подходит для TPL или Data Workflows, чем для Rx, то есть вы фактически связываете продолжения работы вместо того, чтобы реагировать на последовательность событий.

1 голос
/ 21 декабря 2011
invokeClients(param)
    .Where(x => !CheckResult(x))
    .Select(invokeCachedClients)
    .Do(_ => IsBusy = true)
    .Merge()
    .Do(_ => IsBusy = false)
    .Subscribe(x => Console.WriteLine("Do something here"));

Убедитесь, что у вас есть подписка, иначе она не будет работать (это было бы как если бы Foreach не выполнял запрос LINQ)

0 голосов
/ 22 декабря 2011

С такими вещами вы всегда хотите выйти наизнанку.Самое сложное в переводе это части между асинхронными вызовами.Если бы вы передавали результат одного прямо в другой, это было бы просто 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);

Обратите внимание, что я установил для IsBusy значение false как в OnError, так и в OnCompleted, поскольку любое из них является завершающим сообщением дляIObservable.

TPL кажется более естественным подходом для асинхронных методов, подобных этому, так как они выдают только одно значение, но до следующей версии языков с async / await вы, вероятно, получите синтаксис, аналогичный любомуваш метод или мой, если вы используете Tasks вместо IObservables.

...