簡體   English   中英

如何使此Observable更可重用?

[英]How can I make this Observable more reusable?

這是一個可觀察到的序列,可從Web服務檢索分頁數據。 每個Web服務響應都包含一個nextRecordsUrl ,用於指示從何處獲取下一組記錄。 將此Observable轉換為更可重用的最佳方法是什么?

Web服務設置:

var auth = new AuthenticationClient ();
await auth.UsernamePasswordAsync (consumerKey, consumerSecret, userName, password + passwordSecurityToken);
var forceClient = new ForceClient (auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);

可觀察的:

var observable = Observable.Create<QueryResult<Account>> (async (IObserver<QueryResult<Account>> o) =>
{
    try
    {
        var queryResult = await forceClient.QueryAsync<Account> ("SELECT Id, Name from Account");
        if (queryResult != null)
        {
            o.OnNext (queryResult);

            while (!string.IsNullOrEmpty (queryResult.nextRecordsUrl))
            {
                queryResult = await forceClient.QueryContinuationAsync<Account> (queryResult.nextRecordsUrl);
                if (queryResult != null)
                {
                    o.OnNext (queryResult);
                }
            }
        }
        o.OnCompleted ();
    }
    catch (Exception ex)
    {
        o.OnError (ex);
    }

    return () => {};
});

訂閱可觀察者並收集結果:

   var accounts = new List<Account> ();
    observable.Subscribe (
        observer => accounts.AddRange (observer.records),
        ex => Console.WriteLine (ex.Message),
        () => {});

編輯:使用布蘭登的解決方案,我現在可以生成匯總結果列表

List<Account> accounts = await forceClient.QueryPages<Account> ("SELECT Id, Name from Account")
                .Aggregate (new List<Account> (), (list, value) =>
                {
                    list.AddRange (value.records);
                    return list;
                });

您正在尋找類似這樣的東西嗎?

public static IObservable<QueryResult<TResult>> QueryPages<TResult>(this ForceClient forceClient, string query)
{
    return Observable.Create<QueryResult<T>> (async (observer, token) =>
    {
        // No need for try/catch.  Create() will call OnError if your task fails.
        // Also no need for OnCompleted().  Create() calls it when your task completes
        var queryResult = await forceClient.QueryAsync<TResult> (query);
        while (queryResult != null)
        {
            observer.OnNext (queryResult);

            // check the token *after* we call OnNext
            // because if an observer unsubscribes
            // it typically occurs during the notification
            // e.g. they are using .Take(..) or
            // something.
            if (string.IsNullOrEmpty(queryResult.nextRecordsUrl) ||
                token.IsCancellationRequested)
            {
                break;
            }

            queryResult = await forceClient.QueryContinuationAsync<TResult> (queryResult.nextRecordsUrl);
        }
        // No need to return anything.  Just the Task itself is all that Create() wants.
    }
});

// Usage:
var forceClient = // ...
var foos = forceClient.QueryPages<Foo>("SELECT A, B, C FROM Foo");

請注意,我將其切換為提供取消令牌的重載,以便在觀察者退訂時可以停止獲取頁面(即使觀察者已停止監聽,您的原始版本也會繼續獲取頁面)。 還要注意,異步Create等待您的Task並為您調用OnErrorOnCompleted ,因此您無需擔心大多數時間。

信不信由你, Rx-Expiremental庫(也由MS維護)具有一個名為Expand的運算符。 Expand用於從可觀察對象中獲取每個元素,並通過一個函數運行它,該函數產生另一個相同類型的可觀察對象。 然后將可觀察的數據平化為原始圖像,然后將其中的每個項目都經過相同的過程。

想象得到一個帶有可觀察子節點的樹節點。 您可以使用expand輕松遍歷此樹。 由於鏈接列表只是樹的受限版本,並且由於您擁有的實際上是鏈表,其中每個節點都是可觀察的,因此可以使用expand。

public static IObservable<QueryResult<TResult>> QueryPages<TResult>(this ForceClient forceClient, string query)
{
    return Observable.FromAsync(() => forceClient.QueryAsync<TResult>(query))
        .Where(QueryResultIsValid)
        .Expand(result =>
            Observable.FromAsync(() => forceClient.QueryContinuationAsync<TResult>(queryResult.nextRecordsUrl))
                .Where(QueryResultIsValid)
        );
}

public static bool QueryResultIsValid(QueryResult<TResult> result)
{
    return result != null;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM