C# 故障时暂停基于计时器的操作

C# 故障时暂停基于计时器的操作,c#,system.reactive,reactiveui,rx.net,C#,System.reactive,Reactiveui,Rx.net,我有一个使用ReactiveUI的WPF应用程序,它通过定期从外部进程获取状态来工作 给定一个可观察的回程,如下所示: var-latestState= 可观测间隔(时间跨度从秒(.5)) .SelectMany(异步) { 尝试 { var state=await_robotsClient.GetRobotsStateAsync(新的GetRobotsStateRequest()); 返回状态; } 捕获(例外) { 返回null; } }) .Publish(); 如果数据提取失败,我需要

我有一个使用ReactiveUI的WPF应用程序,它通过定期从外部进程获取状态来工作

给定一个可观察的回程,如下所示:

var-latestState=
可观测间隔(时间跨度从秒(.5))
.SelectMany(异步)
{
尝试
{
var state=await_robotsClient.GetRobotsStateAsync(新的GetRobotsStateRequest());
返回状态;
}
捕获(例外)
{
返回null;
}
})
.Publish();
如果数据提取失败,我需要能够中断数据提取

希望能够做到的是:

var-latestState=
可观测间隔(时间跨度从秒(.5))
.SelectMany(异步)
{
尝试
{
var state=await_robotsClient.GetRobotsStateAsync(新的GetRobotsStateRequest());
返回状态;
}
捕获(例外)
{
//显示并等待对话框
//提供了启动外部过程的说明等
wait dialogs.ShowErrorMessageAsync(“获取信息失败”,“获取最新状态失败”);
/*缺失:
*一些神奇的gubbins可以在稳定的时间间隔内产生状态,但仍然支持
*显示对话框并停止
*/
返回null;
}
})
.Publish();
显然这是不可行的,因为你最终会遇到鸡和蛋的问题

我尝试对此进行切片的每一种方法(例如,使用
主题
跟踪成功/失败)最终都导致了这样一个事实,即失败案例仍然需要能够发出一个可观察的间隔,尊重故障处理-但这在处理程序内部是不可能的

我几乎可以肯定,这是一个概念化错误信号/检索数据/恢复间隔方式的问题


基于意见反馈的部分解决方案/实施:
var stateTimer=Observable.Interval(TimeSpan.FromSeconds(10));
var状态获取程序=
可观察的。fromsync(async()=>
wait _robotsClient.getrobotsstatesync(新的GetRobotsStateRequest());
IObservable DisplayStateError(异常导致异常)
=>Observable.fromsync(异步()=>
{
wait dialogs.ShowErrorMessageAsync(
“无法获取机器人信息”,
“出了问题”);
返回新的GetRobotsStateReply{};
});
var状态流=
状态计时器
.SelectMany(状态获取程序)
.Catch((异常ex)=>DisplayStateError(ex))
.Publish();
stateStream.Connect();
这个实现可以让我获得所需的行为,并且在显示错误对话框时不会触发计时器;然而,它不会在关闭对话框后触发(我相信,因为流已经被终止了)——我将在评论中使用建议来解决这个问题,然后添加一个答案


工作解决方案(如果重新打开,可以添加为答案)

var fetchTimer=Observable.Timer(TimeSpan.FromSeconds(5));
var statefacher=Observable.fromsync(async()=>
等待U robotsClient.getRobotsStateStatAsync(new GetRobotsStateRequest());
var timerFetch=Observable.SelectMany(fetchTimer,stateFetcher);
IObservable GetErrorHandler(异常ex)=>
可观察的。fromsync(async()=>
{
wait dialogs.ShowErrorMessageAsync(
“测试”,
“测试”);
返回(GetRobotsStateReply)null;
});
IObservable GetStateFetchCycleObservable(
IObservable源)=>
来源
.Catch((异常ex)=>GetErrorHandler(ex))
.SelectMany(状态=>
状态!=null
?GetStateFetchCycleObservable(timerFetch)
:GetStateFetchCycleObservable(stateFetcher));
最晚状态变量=
GetStateFetchCycleObservable(timerFetch)
.Publish();
多亏了西奥多的建议,我终于想出了一个解决办法

我犯了一个错误,没有考虑热/冷观测,没有正确使用内置的错误处理机制

我最初使用的是
Observable.Interval
,但这产生了一个不希望出现的后果,即在前一个远程请求仍在运行时触发并启动一个新的远程请求(我想我可能已经限制了)

此解决方案通过使用
可观察计时器
设置初始延迟,然后发出远程请求;然后观察该流,出错时显示对话框,然后绑定回delay+fetch流

由于delay+fetch流是冷的,因此延迟会按照预期再次工作,并且所有内容都会在一个很好的循环中返回

这一点已经得到了进一步的解决,因为出现了两次触发计时器(使用
重试时)的问题,或者在对话框关闭后第二次不执行任何操作的问题

我意识到这是由于内部可观察物没有外部可观察物的投影回到一个产生价值的可观察物

新的解决方案解决了这一问题,甚至解决了在用户关闭对话框时立即重新获取状态的问题,或者在结果成功时用时间间隔填充状态的问题。

以下是我的建议:

var observable = Observable
    .Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(500))
    .Select(x => Observable.FromAsync(async () =>
    {
        return await _robotsClient.GetRobotsStateAsync(new GetRobotsStateRequest());
    }))
    .Concat()
    .Catch((Exception ex) => Observable.FromAsync<GetRobotsStateReply>(async () =>
    {
        await dialogs.ShowErrorMessageAsync("Failed to fetch info",
            "Failed to get the latest state");
        throw ex;
    }))
    .Retry();
var可观测=可观测
.Timer(TimeSpan.Zero,TimeSpan.From毫秒(500))
.Select(x=>Observable.fromsync(async()=>
{
return wait_robotsClient.getrobotsstatesync(新的GetRobotsStateRequest());
}))
.Concat()
.Catch((异常ex)=>Observable.fromsync(异步()=>
{
wait dialogs.ShowErrorMessageAsync(“获取信息失败”,
“未能获取最新状态”);
掷骰子;
}))
.重试();
定时器
+
选择