避免在可观察的管道中多次调用

时间:2019-04-03 00:38:31

标签: system.reactive akavache

我正在尝试创建一个GetAndFetch方法,该方法将首先从缓存中返回数据,然后从Web服务获取并返回数据,最后更新缓存。

这样的功能已经存在于akavache中,但是,由它检索或存储的数据就像一个Blob。也就是说,如果我对rss Feed感兴趣,那么我只能在整个Feed级别上工作,而不能在单个项目上工作。我对创建一个将项目返回为IObservable<Item>的版本感兴趣。这样做的好处是,新的Item可以在service返回后立即显示,而不必等待所有的Items

public IObservable<Item> GetAndFetch(IBlobCache cache, string feedUrl)
{
    // The basic idea is to first get the cached objects
    IObservable<HashSet<Item>> cacheBlobObject = cache.GetObject<HashSet<Item>>(feedUrl);

    // Then call the service   
    IObservable<Item> fetchObs = service.GetItems(feedUrl);

    // Consolidate the cache & the retrieved data and then update cache
    IObservable<Item> updateObs = fetchObs
                                      .ToArray()
                                      .MyFilter() // filter out duplicates between retried data and cache
                                      .SelectMany(arg =>
                                      {
                                          return cache.InsertObject(feedUrl, arg)
                                          .SelectMany(__ => Observable.Empty<Item>());
                                      });

    // Then make sure cache retrieval, fetching and update is done in order
    return cacheBlobObject.SelectMany(x => x.ToObservable())
                .Concat(fetchObs)
                .Concat(upadteObs);
}

我的方法存在的问题是Concat(upadteObs)重新订阅了fetchObs,最终再次调用了service.GetItems(feedUrl),这很浪费。

1 个答案:

答案 0 :(得分:1)

您听起来好像需要.Publish(share => { ... })重载。

尝试一下:

public IObservable<Item> GetAndFetch(IBlobCache cache, string feedUrl)
{
    // The basic idea is to first get the cached objects
    IObservable<HashSet<Item>> cacheBlobObject = cache.GetObject<HashSet<Item>>(feedUrl);

    return
        service
            .GetItems(feedUrl)
            .Publish(fetchObs =>
            {
                // Consolidate the cache & the retrieved data and then update cache
                IObservable<Item> updateObs =
                    fetchObs
                        .ToArray()
                        .MyFilter() // filter out duplicates between retried data and cache
                        .SelectMany(arg =>
                            cache
                                .InsertObject(feedUrl, arg)
                                .SelectMany(__ => Observable.Empty<Item>()));

                // Then make sure cache retrieval, fetching and update is done in order
                return
                    cacheBlobObject
                        .SelectMany(x => x.ToObservable())
                        .Concat(fetchObs)
                        .Concat(updateObs);
            });
}

我担心Concat通话-他们可能需要Merge

此外,您对service.GetItems的调用似乎仍在获取所有项目-如何避免缓存中已有的项目?


基于注释的另一种实现方式:

public IObservable<Item> GetAndFetch(IBlobCache cache, string feedUrl)
{
    return
    (
        from hs in cache.GetObject<HashSet<Item>>(feedUrl)
        let ids = new HashSet<string>(hs.Select(x => x.Id))
        select
            hs
                .ToObservable()
                .Merge(
                    service
                        .GetItems(feedUrl)
                        .Where(x => !ids.Contains(x.Id))
                        .Do(x => cache.InsertObject(feedUrl, new [] { x })))
    ).Merge();
}