简体   繁体   English

如何使此Observable更可重用?

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

This is an observable sequence that retrieves paginated data from a web service. 这是一个可观察到的序列,可从Web服务检索分页数据。 Each web service response contains a nextRecordsUrl that indicates where to get the next set of records. 每个Web服务响应都包含一个nextRecordsUrl ,用于指示从何处获取下一组记录。 What is the best way to convert this Observable to something more reusable? 将此Observable转换为更可重用的最佳方法是什么?

Web services setup: Web服务设置:

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

The Observable: 可观察的:

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 () => {};
});

Subscribing to the observable and collecting the results: 订阅可观察者并收集结果:

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

EDIT: Using Brandon's solution I can now generate the list of results with Aggregate 编辑:使用布兰登的解决方案,我现在可以生成汇总结果列表

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

Is something like this what you are looking for? 您正在寻找类似这样的东西吗?

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

Notice I switched it to the overload that provides a cancellation token so that you can stop fetching pages if the observer unsubscribes (Your original version would have continued fetching pages even though the observer had stopped listening). 请注意,我将其切换为提供取消令牌的重载,以便在观察者退订时可以停止获取页面(即使观察者已停止监听,您的原始版本也会继续获取页面)。 Also note that the async Create awaits your Task and calls OnError or OnCompleted for you so you do not need to worry about that most of the time. 还要注意,异步Create等待您的Task并为您调用OnErrorOnCompleted ,因此您无需担心大多数时间。

Believe it or not, the Rx-Expiremental library (also maintained by MS) has an operator for this called Expand . 信不信由你, Rx-Expiremental库(也由MS维护)具有一个名为Expand的运算符。 Expand is used to take each element from an observable and run it through a function which produces another observable of the same type. Expand用于从可观察对象中获取每个元素,并通过一个函数运行它,该函数产生另一个相同类型的可观察对象。 That observable is then flattened in to the original, and each item from that goes through the same process. 然后将可观察的数据平化为原始图像,然后将其中的每个项目都经过相同的过程。

Imagine being given a tree node with an observable of child nodes. 想象得到一个带有可观察子节点的树节点。 You could use expand to easily traverse this tree. 您可以使用expand轻松遍历此树。 Since a linked-list is just a constrained version of a tree, and since what you have is effectively a linked list where each node is an observable, you can use 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