RxNet分页

时间:2016-06-22 19:10:23

标签: system.reactive reactive-programming

更新:签名和类型定义

GetChecklistStringParents

IObservable<ChecklistStringParents_Response> GetChecklistStringParents(int company_id, string auth_token, string q = null, int page = 1)

代表页面的顶级响应

public class ChecklistStringParents_Response
{
    //the content of the page a list of string parents
    public List<Resp_ChecklistStringParent> BODY { get; set; }
    public Resp_Meta META { get; set; }
    public List<object> ERRORS { get; set; }
}

单个字符串父级的响应类

public class Resp_ChecklistStringParent
{
    public int checklist_string_type { get; set; }
    public string client_date_created { get; set; }
    public string uploaded_date { get; set; }
    public string last_modified { get; set; }
    public string value { get; set; }
    public int id { get; set; }
}

我试图反应(RxNet)访问我已构建的REST API,但是我对这个范例很新,而且有点卡住了。

以下函数逐页请求端点数据。

使用

Observable.DoWhileObservable.Defer以便为每个页面创建一个新的observable,我们会一直创建它们,直到我们收到一个空列表 作为页面回复的主体。

我订阅Observable.DoWhile返回的observable来更新页面并更新结果计数。这感觉不对,但我还没有看到替代方案。

我的问题是,这是在RxNet中分页结果的最佳方法吗?另外,我真的想得到一个结果流,即每个页面的内容被展平为一个可观察的,我可以从这个函数返回,但我不知道如何实现这个。

private void FetchStringParents(int checklist_id)
    {
        /*Setup the filter to the endpoint such that string parents returned are those associated with the passed checklist*/

        Filter base_filter = new Filter("checklist_id", "eq", checklist_id.ToString());
        NestedFilter nested_filter = new NestedFilter("checklists", "any", base_filter);
        Filters filt = new Filters();
        filt.filters.Add(nested_filter);
        string sp_query_json = JsonConvert.SerializeObject(filt);
        int result_count = 0;
        int page = 1;
        var paged_observable = Observable.DoWhile(
            //At least once create an observable on the api endpoint
            Observable.Defer(() => NetworkService_ChecklistStringParentService.GetInstance.GetChecklistStringParents(6,
             this.cached_auth.Auth_Token, sp_query_json, page)),
        /*boolean function which determines if we should hit the api again   (if we got back a non empty result the last time)*/
        () => result_count > 0)
        .Subscribe(
             st =>
             {
                 //on successful receipt of a page
                 Debug.WriteLine("Success");
                 page++;//update page so that the next observable created is created on page++
                 result_count = st.BODY.Count;//update result_count (we will stop listening on a count of 0)
             },
              _e =>
              {
                  Debug.WriteLine("Fail");
              });
    }

更新:解决方案

private IObservable<Resp_ChecklistStringParent> StreamParents(int checklist_id)
    {
        Filter base_filter = new Filter("checklist_id", "eq", checklist_id.ToString());
        NestedFilter nested_filter = new NestedFilter("checklists", "any", base_filter);
        Filters filt = new Filters();
        filt.filters.Add(nested_filter);
        string sp_query_json = JsonConvert.SerializeObject(filt);
        return Observable.Create<List<Resp_ChecklistStringParent>>(async (obs, ct) =>
        {
            int pageIdx = 1;
            //for testing page size is set to 1 on server
            int pageSize = 1;
            while (!ct.IsCancellationRequested)
            {
                //Pass in cancellation token here?
                var page = await NetworkService_ChecklistStringParentService.GetInstance.GetChecklistStringParents(6,
                this.cached_auth.Auth_Token, sp_query_json, pageIdx++);
                obs.OnNext(page.BODY);
                if (page.BODY.Count < pageSize)
                {
                    obs.OnCompleted();
                    break;
                }
            }
        })
        .SelectMany(page => page);
    }

1 个答案:

答案 0 :(得分:2)

以下示例展示了如何使用IEnumerable<T>IObservable<T>来传输数据。 IEnumerable<T>样本用于上下文和比较。

我没有使用您的数据类型,因为我认为它与问题无关(希望没问题)。

Linqpad脚本

void Main()
{
    var enumerableItems = GetItemsSync();
    foreach (var element in enumerableItems)
    {
        Console.WriteLine(element);
    }
    Console.WriteLine("Received all synchronous items");
    StreamItems().Subscribe(
        element => Console.WriteLine(element),
        () => Console.WriteLine("Received all asynchronous items"));
}

// Define other methods and classes here

public IObservable<string> StreamItems()
{
    return Observable.Create<string[]>(async (obs, ct) =>
    {
        var pageIdx = 0;
        var pageSize = 3;
        while (!ct.IsCancellationRequested)
        {
            //Pass in cancellation token here?
            var page = await GetPageAsync(pageIdx++, pageSize);
            obs.OnNext(page);
            if (page.Length < pageSize)
            {
                obs.OnCompleted();
                break;
            }
        }
    })
    .SelectMany(page => page);
}

public IEnumerable<string> GetItemsSync()
{
    return GetPagesSync().SelectMany(page => page);
}
public IEnumerable<string[]> GetPagesSync()
{
    var i = 0;
    var pageSize = 3;
    while (true)
    {
        var page = GetPageSync(i++, pageSize);
        yield return page;
        if (page.Length < pageSize)
            yield break;
    }
}
private static string[] _fakeData = new string[]{
    "one",
    "two",
    "three",
    "four",
    "Five",
    "six",
    "Se7en",
    "Eight"
};
public string[] GetPageSync(int pageIndex, int pageSize)
{
    var idx = pageSize * pageIndex;
    var bufferSize = Math.Min(pageSize, _fakeData.Length-idx);
    var buffer = new string[bufferSize];
    Array.Copy(_fakeData, idx, buffer, 0, bufferSize);
    return buffer;
}
public Task<string[]> GetPageAsync(int pageIndex, int pageSize)
{
    //Just to emulate an Async method (like a web request).
    return Task.FromResult(GetPageSync(pageIndex, pageSize));
}