代码之家  ›  专栏  ›  技术社区  ›  theburningmonk

使用Postharp在异常时重试方法

  •  1
  • theburningmonk  · 技术社区  · 16 年前

    对于我的一个DAL模块,我有很多重复的管道,形状如下:

    while (retry)
    {
    ...
    try
    {
       ...do something
       retry = false;
    }
    catch (SqlException sqlEx)
    {
       // Retry only if -2 = Connection Time Out or 1205 = Deadlock
      if (sqlEx.Number == -2 || sqlEx.Number == 1205)
      {
          ..retry if attempt < max
      }
          ..log and rethrow exception
    }
    }
    

    最近发现了postsharp,我正试图用一个属性替换这些管道代码。

    我最初的计划是: -在方法调用期间扩展onMethodInvocationAspect并记住方法调用事件参数 -实现IOExceptionAspect并实现OnException以检查异常类型,如果需要重试,请使用来自原始调用的方法调用事件args对象,即:

    [Serializable]
    public sealed class RetryAttribute : OnMethodInvocationAspect, IOnExceptionAspect
    {
        [NonSerialized]
        private MethodInvocationEventArgs m_initialInvocationEventArgs = null;
    
        public override void OnInvocation(MethodInvocationEventArgs eventArgs)
        {
            if (m_initialInvocationEventArgs == null)
                m_initialInvocationEventArgs = eventArgs;
    
            base.OnInvocation(eventArgs);
        }
    
        public void OnException(MethodExecutionEventArgs eventArgs)
        {
            // check if retry is necessary
            m_initialInvocationEventArgs.Proceed();
        }
    }
    

    但是一旦我添加了IOnexceptionAspect,OnInvocation方法就不再被激发。

    有人知道我要在这里做什么吗?或者也许我应该使用更合适的方面?

    谢谢,

    3 回复  |  直到 14 年前
        1
  •  4
  •   Gael Fraiteur    16 年前

    您不能有实现两个方面接口的方面(在您的案例中是IonMethodInvocation和IonExceptionAspect)。weaver将采用一个任意的接口并实现这个方面。

    我认为你实现目标所需要的只是有条理的监视。为什么你不把for循环和try-catch放在oninvocation处理程序中?

    盖尔

        2
  •  1
  •   Kemal Taşkın    14 年前

    一个相当古老的问题,但我想分享一种可能会有所帮助的方法。我们也成功地使用了这个范例。请看下面的代码。你所做的实际上是继承自 MethodLevelAspect 使用“建议”。

    OnException 无济于事 args.Proceed() 抛出错误。因此,我们在 OnInvoke .

    [Serializable]
    public class MyAspectAttribute : MethodLevelAspect
    {
        object exceptionReturn = null;
    
        public MyAspectAttribute(object ExceptionReturn) : base()
        {
        }
    
        [OnMethodInvokeAdvice, SelfPointcut]
        [AdviceDependency(AspectDependencyAction.Order, AspectDependencyPosition.Before, "OnEntry")]
        public void OnInvoke(MethodInterceptionArgs args)
        {
            try
            {
                args.Proceed();
            }
            catch (Exception exc)
            {
                // do logging here
                args.ReturnValue = exceptionReturn;
            }
        }
    
        [OnMethodExceptionAdvice, SelfPointcut]
        public void OnException(MethodExecutionArgs args)
        {
        }
    
        [OnMethodEntryAdvice, SelfPointcut]
        public void OnEntry(MethodExecutionArgs args)
        {
        }
    
        [OnMethodExitAdvice, SelfPointcut]
        [AdviceDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, "OnInvoke")]
        [AdviceDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, "OnEntry")]
        public void OnExit(MethodExecutionArgs args)
        {
             // your exit statements, such as committing transaction etc.
        }
    }
    
        3
  •  0
  •   Allon Guralnek    16 年前

    这里有一个相当简单的解决方案,不涉及Postshap。创建以下实用程序方法。

    public static void Try(Func<bool> task, int retryCount)
    {
        int attemptNum = 1;
        while (attemptNum++ <= retryCount && task()) ;
    }
    

    然后创建要重试的任务。返回值应指示是否应尝试重试。

    public bool UnreliableTask()
    {
        try
        {
            // Do something
        }
        catch (SqlException ex)
        {
            return (ex.Number == -2 || ex.Number == 1205);
        }
    
        return false;
    }
    

    然后像这样调用任务,以重试五次:

    Try(UnreliableTask, 5);