拍摄IObservable <t> </t>的快照

时间:2014-01-09 04:38:15

标签: c# .net system.reactive

假设我有服务:

public interface ICustomersService
{
    IObservable<ICustomer> Customers
    {
        get;
    }
}

Customers属性的实现首先抓住所有现有客户并将其传递给观察者,之后它只传递稍后添加到系统中的客户。因此,它永远不会完成。

现在假设我想获取当前客户的快照(作为List<ICustomer>),忽略将来可能添加的任何快照。我怎么做?任何ToList()或其亲属的调用都将永久阻止,因为序列永远不会完成。

我想我可以编写自己的扩展程序,所以我尝试了这个:

public static class RxExtensions
{
    public static List<T> ToSnapshot<T>(this IObservable<T> @this)
    {
        var list = new List<T>();

        using (@this.Subscribe(x => list.Add(x)));

        return list;
    }
}

这似乎有效。例如:

var customers = new ReplaySubject<string>();

// snapshot has nothing in it
var snapshot1 = customers.ToSnapshot();

customers.OnNext("A");
customers.OnNext("B");

// snapshot has just the two customers in it
var snapshot2 = customers.ToSnapshot();

customers.OnNext("C");

// snapshot has three customers in it
var snapshot3 = customers.ToSnapshot();

我意识到当前的实现取决于调度程序是当前线程,否则ToSnapshot可能会在收到项目之前关闭其订阅。但是,我怀疑我还可以包含一个ToSnapshot覆盖,它会占用IScheduler并确保在结束快照之前收到任何已安排的项目。

我找不到Rx中内置的这种快照功能。我错过了什么吗?

3 个答案:

答案 0 :(得分:1)

您可以尝试在可观察的

上使用超时
source.Customers().TakeUntil(DateTime.Now).ToEnumerable();

答案 1 :(得分:1)

有几种方法可以解决这个问题。我在商业项目中尝试了以下成功:

1)一种单独的方法来获得克里斯所展示的现有客户的数量。

2)将“世界状态”调用与实时流相结合的方法 - 这比Chris的示例更为复杂,因为为了确保没有错过的数据,通常必须首先开始收听实时流,然后获取快照,然后将两者合并为重复数据删除。

我使用自定义Observable.Create实现来实现此目标,该实现缓存实时流,直到检索到历史记录,然后在切换到实时之前将缓存与历史记录合并。

这返回了Customers,但包含了描述数据年龄的其他元数据。

3)最近,对于我来说,返回IObservable<IEnumerable<Customer>>对第一个事件是整个世界状态更为有用。这个更有用的原因是我工作的许多系统都会批量更新,而且通过逐项更新整个批处理的UI通常会更快。它与(2)类似,只是你可以使用FirstAsync()来获取所需的快照。

我建议你考虑这种方法。如果需要,您始终可以使用SelectMany(x => x)IObservable<IEnumerable<Customer>>的流展平为IObservable<Customer>

当我回到家庭办公室时,我会看看是否可以挖掘一个示例实现!

答案 2 :(得分:0)

你在这里所做的实际上非常漂亮。 ToSnapshot工作的原因是因为订阅逻辑的底层实现在释放控制流之前将所有客户都交给了观察者。基本上,Dispose仅在控制流被释放后被调用,并且控制流仅在您已经产生所有预先存在的联系人之后才被释放。

虽然这是,但这也是一种误导。您编写的方法ToSnapshot应该真正命名为TakeSyncronousNotifications。扩展正在对基础可观察量的工作方式做出重大假设,而且并非真正符合Rx的精神。

为了让消费者更容易理解,我会公开其他属性,明确说明要返回的内容。

public interface ICustomersService
{
    IEnumerable<ICustomer> ExistingCustomers { get; }
    IObservable<ICustomer> NewCustomers { get; }
    IObservable<ICustomer> Customers { get; }
}

public class CustomerService : ICustomerService
{
    public IEnumerable<ICustomer> ExistingCustomers { get { ... } }
    public IObservable<ICustomer> NewCustomers { get { ... } }
    public IObservable<ICustomer> Customers
    {
        get
        {
           return this.ExistingCustomers.ToObservable().Concat(this.NewCustomers);
        }
    }
}

编辑:

考虑以下问题...

50 = x + y。解决并评估x

除非你知道y是什么,否则数学不起作用。在此示例中,y是“新客户”,x是“现有客户”,50是两者的组合。

通过仅公开现有客户和新客户的组合,而不是现有客户和新客户,您丢失了太多数据。您需要向消费者公开至少xy,否则无法解决另一方。