在响应式扩展中使用异常作为数据传输对象

时间:2015-05-15 20:54:32

标签: .net system.reactive

在Rx中,有三种主要方法:OnNext<T>OnErrorOnComplete,只有OnNext才能传递数据。

我有时间标记数据点和数据流,其中有正常值通过OnNext传递并占99%以上的数据点。但是,一些数据点是例外的,例如延迟数据或无序数据点重写历史记录。逻辑上,对于需要特殊处理的数据,它是下一个事件,而不是预期顺序中的下一个数据。

在这种情况下,通过OnError频道传递特殊值是“正常”吗?我可以使用类型OutOfOrderDataExcpetion<T> : Exception的属性定义T并在观察者中处理它。鉴于异常值很少,我不抛出/引发异常但构造它们并将它们用作DTO,性能应该不是问题。

我担心的是该方法被称为OnError而不是OnException,但我的数据不是错误,只需要特殊处理。同时,如果我将我的observable暴露给对OutOfOrderExcpetion<T>一无所知的外部代码,那么外部代码将无法专门捕获它,这将是一个错误。

我可以遵循事件源模式并使用命令和有效负载将每个数据事件包装在包装器中,但是我需要解压缩每个命令,在99%以上的情况下,它将是相同的命令“next”。使用OnError作为所有其他命令的通道,通过简化处理为我提供了几乎相同的事件源模式。

除了命名问题,您是否可以添加一些会阻止我使用此设计的参数?或者这是对异常的正确使用?

2 个答案:

答案 0 :(得分:2)

Rx observables有行为合约。保证您可以进行零次或多次OnNext次呼叫,然后是OnErrorOnCompleted中的一次和一次呼叫。

如果您使用OnError,则表示“出现问题,现在序列已经结束”

所以你真的不能使用OnError,因为它不是“错误频道”。

如果你这样做的话,使用包装器会很简单:

normals
    .Select(data => new { normal = true, data })
    .Merge(
        specials
            .Select(data => new { normal = false, data })

答案 1 :(得分:1)

绝对不要使用OnError来传递数据。正如你自己暗示的那样,可能会发生不好的事情。

我不太清楚使用包装器有什么问题。你说你不想编写解包代码,但当然,不愿意做不正确的事情是一个非常奇怪的原因。这有客观原因吗?

您是否担心解包代码会使您的程序混乱?但是你不必在一个地方把它全部写下来,你可以把它分开但仍然看起来很好看。类似的东西:

IObservable<Wrapper> source = ...
var processed = 
    source
    .HandleNormalCase( unwrappedNormalData => { ... } )
    .HandleAbnormalCase( unwrappedExnData => { ... } )

.HandleXxx是执行展开的扩展方法。