C# Rx.Net+;反应式用户界面&x2B;马哈普斯地铁-重复和;使用选通对话框重试异步请求
给定一个可观察的形式:C# Rx.Net+;反应式用户界面&x2B;马哈普斯地铁-重复和;使用选通对话框重试异步请求,c#,system.reactive,reactiveui,mahapps.metro,rx.net,C#,System.reactive,Reactiveui,Mahapps.metro,Rx.net,给定一个可观察的形式: var fetchTimer=Observable.Timer(TimeSpan.FromSeconds(1)); var状态获取程序= Observable.FromAsync(async()=>Wait _robotsClient.GetRobotsStateAync(new GetRobotsStateRequest()); var delayedFetch=fetchTimer.SelectMany(stateFetcher); 这提供了在延迟后获取状态的方法
var fetchTimer=Observable.Timer(TimeSpan.FromSeconds(1));
var状态获取程序=
Observable.FromAsync(async()=>Wait _robotsClient.GetRobotsStateAync(new GetRobotsStateRequest());
var delayedFetch=fetchTimer.SelectMany(stateFetcher);
这提供了在延迟后获取状态的方法
修改可以定期执行此操作:
var regularFetch=Observable.Interval(TimeSpan.FromSeconds(5)).Select(=>stateFetcher.Switch();
这将每5秒请求一个值
但是,请求可能会失败(远程服务无法访问等);考虑到这一点,尝试生成重试操作的机制以及提醒用户的钩子可能会很棘手
-我提出的这一问题涵盖了解决这一问题的初步方法,以及部分尝试/部分解决方案
在这里,我想分享我得出的解决方案。我们可以将问题总结如下:
错误
->初始对话框
->每次尝试时通知重试
->再次执行所有操作
在整个过程中的任何一点上,成功的价值排放都应该绕过一切,并流回
考虑到这种高度固执己见的方法,下面是我创建的实用程序:
公共静态IObservable with GatedRetry(
这是一个可观测的来源,
int retriesPerCycle,
Func onInitialFailure,
函数retryNotificationBlock,
函数(失败循环)
{
IObservable GetInitialHandler(异常e)=>
可观察的。FromAsync(()=>onInitialFailure(e))
.选择(=>(T)默认值);
IObservable GetCycleFailureHandler(异常e)=>
可观察的。FromAsync(()=>onFailedCycle(e))
.选择(=>(T)默认值);
IObservable GetRetryFlow()=>
可观察。创建(异步子=>
{
var尝试=1;
Func disposeCallback=()=>Task.CompletedTask;
var通知程序=Wait retryNotificationBlock(dc=>
{
disposeCallback=dc;
});
等待通知程序(null,1);
返回
来源
.做(
_ =>
{
},
异步(异常e)=>
{
如果(尝试+1
{
if(disposeCallback!=null)
{
等待处置回拨();
}
})
.订阅(
val=>{sub.OnNext(val);sub.OnCompleted();},
(例外情况e)=>{sub.OnError(e);}
);
});
IObservable GetCycleFlow()=>
GetRetryFlow()
.Catch((异常e)=>
GetCycleFailureHandler(e)
.Select(=>GetCycleFlow())
.Switch()
)
.重试();
IObservable GetPrimaryFlow()=>
来源
.Catch((异常e)=>GetInitialHandler(e))
.选择(val=>
EqualityComparer.Default.Equals(val,默认值)
?GetCycleFlow()。选择(=>GetPrimaryFlow())。开关()
:GetPrimaryFlow().StartWith(val)
)
.开关();
返回GetPrimaryFlow();
}
我完全承认这可能不是最好的方法,在通知块中(对于每次重试尝试)有一点回调内部回调的混乱,以便在重试“周期”完成后(成功或其他)支持清理
用法如下:
var-latestState=
可观察。SelectMany(fetchTimer、stateFetcher)
.用GatedRetry(
3.
异步ex=>
{
//显示初始错误对话框
},
异步(disperhook)=>
{
//显示“尝试重试”对话框
disperhook(异步()=>
{
//关闭“尝试重试”对话框
//做任何清理工作
});
返回异步(异常ex,int尝试)=>
{
//更新对话框
//ex是刚刚完成的尝试产生的异常
};
},
异步ex=>
{
//显示“我们仍然不能完全理解”对话框
//此任务完成后,整个过程将再次启动
}
)
.Publish();
这种方法允许定制挂钩点,并按预期传递成功值
事实上,下游订阅者应该只在成功提供一个值时看到一个值——他们也不应该看到一个错误,因为它位于无限重试中
与原始问题中的解决方案相比,这使用了
Select
+开关
,而不是SelectMany
,以确保内部观察值被正确地取消订阅和处理。只是一个旁注,不要将async
/wait
放在fromsync
调用中。是否有任何错误伊森:。重试(…)
对你不起作用?@Enigmativity噢?为什么异步/等待
这件事对我来说很有效?在我现在得到的解决方案中,最大的问题是我把计时器和它捆绑在一起,当然它在对话框可见时发出,这导致了问题。可见。fromsync
打开e任务
已经完成。