C# 如何将异常转换为事件并重新订阅故障IObservable?

C# 如何将异常转换为事件并重新订阅故障IObservable?,c#,system.reactive,C#,System.reactive,如何将IObservable流中的异常转换为普通域对象并透明地重新订阅流 附录:正如评论中所指出的,我的用例想法类似于在不可靠的源(如网络)上拥有一个应该是连续的流。如果出现故障,请尝试重新连接到源,但通知下游处理器 事实上,这与我的另一个问题有关,而这个问题又源于 事实上,现在我想到了它,我可以首先使用(“”)处的代码(带有更多的参数来调整RetryAfterDelay行为),并将其与此实现链接起来。当重试次数用尽时,将产生域错误并重新初始化轮询器。诚然,可能会有一种更有效的方法,但无论如何。

如何将
IObservable
流中的异常转换为普通域对象并透明地重新订阅流

附录:正如评论中所指出的,我的用例想法类似于在不可靠的源(如网络)上拥有一个应该是连续的流。如果出现故障,请尝试重新连接到源,但通知下游处理器

事实上,这与我的另一个问题有关,而这个问题又源于

事实上,现在我想到了它,我可以首先使用(“”)处的代码(带有更多的参数来调整
RetryAfterDelay
行为),并将其与此实现链接起来。当重试次数用尽时,将产生域错误并重新初始化轮询器。诚然,可能会有一种更有效的方法,但无论如何。。。或者只提供一个回调函数来记录错误,而不是将它们转换为域事件,好吧,选择比比皆是

但是回到原始代码

例如,如果我有

public enum EventTypeEnum
{
    None    = 0,
    Normal  = 1,
    Faulted = 2
}

public class Event
{
    public EventTypeEnum Type { get; set; }
} 

private static IObservable<int> FaultingSequence1()
{
    var subject = new ReplaySubject<int>();
    subject.OnNext(1);
    subject.OnNext(2);
    subject.OnError(new InvalidOperationException("Something went wrong!"));

    return subject;
}

private static IEnumerable<int> FaultingSequence2()
{                           
    for(int i = 0; i < 3; ++i)
    {
        yield return 1;
    }

    throw new InvalidOperationException("Something went wrong!");
}

//Additional pondering: Why isn't FaultingSequence2().ToObservable() too be procted by Catch?
//
//This part is for illustratory purposes here. This is the piece I'd like
//behave so that exceptions would get transformed to Events with EventTypeEnum.Faulted
//and passed along to the stream that's been subscribed to while resubscribing to 
//FaultingSequence1. That is, the subscribed would learn about the fault through a
//domain event type.
//Retry does the resubscribing, but only on OnError.
var stream = FaultingSequence1().Catch<int, Exception>(ex =>
{
    Console.WriteLine("Exception: {0}", ex);
    return Observable.Throw<int>(ex);
}).Retry().Select(i => new Event { Type = EventTypeEnum.Normal });

//How to get this to print "Event type: Normal", "Event type: Normal", "Event type: Faulted"?
stream.Subscribe(i => Console.WriteLine("Event type: {0}", i.Type));
公共枚举事件类型枚举
{
无=0,
正常值=1,
故障=2
}
公开课活动
{
公共事件类型枚举类型{get;set;}
} 
私有静态IObservable FaultingSequence1()
{
var subject=新的ReplaySubject();
主题.OnNext(1);
主题.OnNext(2);
subject.OnError(新的无效操作异常(“出错了!”);
返回主题;
}
私有静态IEnumerable FaultingSequence2()
{                           
对于(int i=0;i<3;++i)
{
收益率1;
}
抛出新的InvalidOperationException(“出错了!”);
}
//额外的思考:为什么FaultingSequence2().ToObservable()不也由Catch处理?
//
//这一部分在这里是为了举例说明。这是我想要的
//行为,以便将异常转换为EventTypeEnum.Faulted的事件
//并在重新订阅时传递到已订阅的流
//断层序列1。也就是说,订阅方将通过
//域事件类型。
//重试不会重新订阅,但仅在OnError上。
var stream=FaultingSequence1().Catch(ex=>
{
WriteLine(“异常:{0}”,ex);
可观测的返回距离(ex);
}).Retry().Select(i=>newevent{Type=EventTypeEnum.Normal});
//如何打印“事件类型:正常”、“事件类型:正常”、“事件类型:故障”?
Subscribe(i=>Console.WriteLine(“事件类型:{0}”,i.type));

这个问题现在真的困扰着我!有什么建议吗?

有一个名为的操作符,它将每个事件转换为:

但是,即使原始订阅自然完成(通过OnCompleted),也会重新订阅源

因此,您可能仍然希望调用OnError,但也希望原始OnError的异常通过
通知中的OnNext传递。为此,您可以使用以下内容:

source
    .Materialize()
    .SelectMany(notification => 
        notification.Kind == NotificationKind.OnError
            ? Observable.Return(notification).Concat(Observable.Exception(notification.Exception))
            : Observable.Return(notification)
    )
    .Retry();
以这种方式,如果订阅自然完成(通过OnCompleted),则不会重新订阅源

设置好后,就可以将每种类型的通知映射到要使用的任何域对象:

source
    .Materialize()
    .SelectMany(notification => 
        notification.Kind == NotificationKind.OnError
            ? Observable.Return(notification).Concat(Observable.Exception(notification.Exception))
            : Observable.Return(notification)
    )
    .Retry()
    .Map(notification => {
        switch (notification.Kind) {
            case (NotificationKind.OnNext):      return // something.
            case (NotificationKind.OnError):     return // something.
            case (NotificationKind.OnCompleted): return // something.
            default: throw new NotImplementedException();
        }
    });

另外,一定要在概念上区分“流中的异常”和以错误终止的流。您在那里选择的语言暗示的不是流本身的故障(这类似于基础设施问题),而是应用程序的业务逻辑的故障。如果您处理的是“业务逻辑”异常,那么这些异常通常最好作为流的数据处理-例如,使用任意一个monad(Rxx库中实现的经典frp概念)。更多…我相信你知道这一点,你说的是真正的“基础设施”类型错误-但我只是想强调这一点,以避免读者陷入不适当使用OneError的陷阱。嗯,我添加了一个关于这一点的注释。当我试图为您的代码生成一个F版本时,我已经筋疲力尽了,但是不要使用
,而是使用
选择
,这是F的等价物。这一切现在都清楚了一点,直到孩子们让我陷入更深的睡眠剥夺,嗯!这是一个非常全面的解释。干杯这个
物化
业务对我来说有些新鲜,但是一个人阅读了很好的解释并学会了!
source
    .Materialize()
    .SelectMany(notification => 
        notification.Kind == NotificationKind.OnError
            ? Observable.Return(notification).Concat(Observable.Exception(notification.Exception))
            : Observable.Return(notification)
    )
    .Retry();
source
    .Materialize()
    .SelectMany(notification => 
        notification.Kind == NotificationKind.OnError
            ? Observable.Return(notification).Concat(Observable.Exception(notification.Exception))
            : Observable.Return(notification)
    )
    .Retry()
    .Map(notification => {
        switch (notification.Kind) {
            case (NotificationKind.OnNext):      return // something.
            case (NotificationKind.OnError):     return // something.
            case (NotificationKind.OnCompleted): return // something.
            default: throw new NotImplementedException();
        }
    });