1

对于我的一个 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 方法了。

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

谢谢,

4

3 回答 3

4

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

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

于 2009-08-21T14:07:05.780 回答
1

一个相当老的问题,但我想分享一种可能有帮助的方法。我们也成功地使用了这个范例。看看下面的代码。您所做的实际上是继承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.
    }
}
于 2012-04-18T06:30:22.427 回答
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);
于 2009-08-21T16:29:29.787 回答