使用PostSharp在Exception上重试方法

时间:2009-08-21 13:23:39

标签: c# aop postsharp

对于我的一个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并记住方法调用期间的方法调用事件args - 实现IOnExceptionAspect并实现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 个答案:

答案 0 :(得分:4)

您不能拥有实现两个方面接口的方面(在您的情况下为IOnMethodInvocation和IOnExceptionAspect)。编织者将采用一个任意接口并实现该方面。

我认为实现目标所需要的只是OnMethodInvocationAspect。为什么不在OnInvocation处理程序中放置for循环和try-catch?

-gael

答案 1 :(得分:1)

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

OnException无法帮助args.Proceed()抛出错误。因此,我们在OnInvoke中直接使用了一个额外的try-catch块。

[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.
    }
}

答案 2 :(得分:0)

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

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);