使用RX进行异常重试

时间:2012-11-20 07:00:40

标签: .net winforms exception-handling system.reactive

我有一个有趣的模式,我尝试使用RX扩展进行异常重试。我有以下代码。

Public Async Function InitializeCommunications() As Task
    Await Observable.Create(Of Unit)(Async Function(observer)
                                         MessageBox.Show("Please press reset")
                                         Await Me.Cockpit.LoadDriverToPLC()
                                         MessageBox.Show("Please press start")
                                         observer.OnNext(Unit.Default)
                                     End Function).Retry()
End Function

代码几乎完美无缺。如果LoadDriverToPLC中抛出异常 然后重新订阅序列并重新运行正文并要求用户 再次按下重置。

此代码只有一个问题。第一次通过MessageBox调用 是正确的模态。这意味着他们骑在主窗口上方,你无法隐藏它们 意外点击主窗口。但是,如果抛出异常而且 重试body,然后MessageBox调用不再是模态的。

我确定这与RX计划有关。确保身体的诀窍是什么? 总是在同一个环境中运行?

我打算稍后将这个模式包装起来,因为我可以将它变得像

那样可靠
Public Async Function InitializeCommunications() As Task
    Await RetryOn(of Exception)(Async Sub()
                                         MessageBox.Show("Please press reset")
                                         Await Me.Cockpit.LoadDriverToPLC()
                                         MessageBox.Show("Please press start")
                                End Sub)
End Function

1 个答案:

答案 0 :(得分:1)

解决方案似乎是捕获同步上下文和 迫使观察者被告知正确的背景,然后这样做 使用相同的上下文重试

Public Async Function InitializeCommunications(msgFeedback As Func(Of String, Task)) As Task
    Dim context = SynchronizationContext.Current
    Await Observable.Create(Of Unit)(
             Async Function(observer)
                 MessageBox.Show("Please press reset")
                 Await Me.Cockpit.LoadMessageLoopToPLC(111)
                 MessageBox.Show("Please press start")
                 observer.OnNext(Unit.Default)
             End Function).ObserveOn(context).Retry()
End Function

另一种,也许更好的方法是在Object上创建一些扩展方法,如此

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FunctionalExtensions
{
    public static class ObjectMixins
    {
        public static T Retry<Ex, T>(this Object This, int count, Func<T> action)
        where  Ex : Exception
        {
            while (true)
            {
                try
                {
                    return action();
                }
                catch(Ex)
                {
                    if (count==0)
                    {
                        throw;
                    }
                    count--;
                }
            }
        }

        public static async Task<T> Retry<Ex, T>(this Object This, int count, Func<Task<T>> action)
        where  Ex : Exception
        {
            while (true)
            {
                try
                {
                    return await action();
                }
                catch(Ex)
                {
                    if (count==0)
                    {
                        throw;
                    }
                    count--;
                }
            }
        }

        public static void Retry<Ex>(this Object This, int count, Action action)
        where  Ex : Exception
        {
            This.Retry<Ex, bool>(count, () => { action(); return true; });
        }

        public static async Task Retry<Ex>(this Object This, int count, Func<Task> action)
        where  Ex : Exception
        {
            await This.Retry<Ex, bool>(count, async () => { await action(); return true; });
        }

        public static void Retry<Ex>(this Object This, Action action)
        where  Ex : Exception
        {
            This.Retry<Ex, bool>(() => { action(); return true; });
        }

        public static T Retry<Ex, T>(this Object This, Func<T> action)
        where Ex : Exception
        {
            while (true)
            {
                try
                {
                    return action();
                }
                catch(Ex)
                {
                }
            }
        }

    }
}

支持同步和异步操作。您可以像下面一样使用它在抛出异常之前重试10次

Public Async Function InitializeCommunications(msgFeedback As Func(Of String, Task)) As Task
    Await Retry(Of Exception)(10, 
        Async Function()
          MessageBox.Show("Please press reset on the NUM control unit then press ok here.")
          Await Me.Cockpit.LoadMessageLoopToPLC(111)
          MessageBox.Show("Please press start on control unit.")
       End Function)
End Function