Использование PostSharp, чтобы повторить метод на Exception - PullRequest
1 голос
/ 21 августа 2009

Для одного из моих модулей 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 и запомнить аргументы события вызова метода во время вызова метода - реализовать 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();
    }
}

но метод OnInvocation больше не запускается после добавления IOnExceptionAspect ..

Кто-нибудь знает, что мне здесь делать? Или, может быть, мне стоит использовать более подходящий аспект?

Спасибо

Ответы [ 3 ]

4 голосов
/ 21 августа 2009

У вас не может быть аспекта, реализующего два аспектных интерфейса (IOnMethodInvocation и IOnExceptionAspect в вашем случае). Ткач возьмет один произвольный интерфейс и реализует аспект.

Я думаю, что все, что вам нужно для достижения вашей цели - это OnMethodInvocationAspect. Почему бы вам не поместить цикл for и try-catch в обработчик OnInvocation?

-gael

1 голос
/ 18 апреля 2012

Довольно старый вопрос, но я хотел бы поделиться подходом, который может помочь. Мы также успешно используем эту парадигму. Посмотрите на код ниже. На самом деле вы наследуете от MethodLevelAspect и используете «Советы».

OnException не помогает args.Proceed() выдать ошибку. Итак, мы использовали дополнительный блок try-catch непосредственно в 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.
    }
}
0 голосов
/ 21 августа 2009

Вот довольно простое решение, которое не включает 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);
...