2009-08-21 6 views
1

Pour un de mes modules DAL J'ai beaucoup de plomberie dupliquée en forme de:En utilisant PostSharp pour réessayer la méthode sur Exception

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

et avoir découvert PostSharp récemment, je suis tenté de remplacer ces codes de plomberie avec un attribut.

Mon plan initial était de: - étendre OnMethodInvocationAspect et rappelez-vous l'événement d'appel de méthode args lors de l'appel de la méthode - mettre en œuvre IOnExceptionAspect et mettre en œuvre OnException pour vérifier le type d'exception et si nouvelle tentative est nécessaire d'utiliser objet l'événement d'appel de méthode args de l'original appel, à savoir:

[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(); 
    } 
} 

mais la méthode de OnInvocation ne se déclenche plus une fois que je l'ai ajouté IOnExceptionAspect ..

est-ce que quelqu'un sait ce que je dois faire ici? Ou peut-être qu'il y a un aspect plus approprié que je devrais utiliser?

Merci,

+1

La question que je voudrais poser est pourquoi êtes-vous ces exceptions (délai d'attente et impasse), et re-conception de l'application pour supprimer l'obligation de rattraper ces - lancer une autre technologie à l'application ne va pas améliorer le design \ implmentation – AwkwardCoder

+0

Eh bien, oui, normalement vous ne devriez pas avoir d'interblocage. Une utilisation typique des tentatives est lorsque vous travaillez dans des transactions concurrentes optimistes. Donc, vous pouvez réessayer, mais je serais d'accord avec Ollie sur ce point. –

+0

Les délais d'attente et les interblocages ne se produisent pas souvent dans notre environnement, mais nous avons tellement d'applications différentes (plus de 100 applications utilisées par des milliers d'utilisateurs dans le monde entier) utilisant le même DB et beaucoup frappent les mêmes tables simultanément. circonstances que nous avons eu des blocages/expirations dans le passé. – theburningmonk

Répondre

4

Vous ne pouvez pas avoir un aspect qui mettent en œuvre deux interfaces (aspect IOnMethodInvocation et IOnExceptionAspect dans votre cas). Le tisserand prendra une interface arbitraire et implémentera l'aspect.

Je pense que tout ce dont vous avez besoin pour atteindre votre objectif est OnMethodInvocationAspect. Pourquoi ne mettez-vous pas la boucle for et le try-catch dans le gestionnaire OnInvocation?

-gael

+0

Merci Gael, bonne idée, essayé et fonctionne un régal :) – theburningmonk

0

est ici une solution assez simple qui ne nécessite pas PostSharp. Créez la méthode d'utilité suivante.

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

Ensuite, créez la tâche à réessayer. La valeur de retour doit indiquer si une nouvelle tentative doit être tentée.

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

    return false; 
} 

Ensuite, il suffit d'appeler la tâche comme si une nouvelle tentative de cinq fois:

Try(UnreliableTask, 5); 
+0

Le problème avec la solution est que vous devez changer le code de toute tâche non fiable. Il peut y en avoir beaucoup. Si vous utilisez PostSharp, vous pouvez utiliser widlcards ou Linq over Reflection pour sélectionner les méthodes auxquelles il s'applique. Cela dit, s'il n'y avait qu'une paire de méthodes non fiables, je préférerais votre approche - programmation fonctionnelle. –

+0

Bon point. Ensuite, la méthode Try pourrait être légèrement modifiée de sorte qu'au lieu d'exiger que les tâches signalent le succès/l'échec via une valeur de retour booléenne, elle interpréterait simplement toute exception non gérée comme un échec, sinon un succès. Cela supprimerait le besoin de modifier les tâches non fiables. Mais bien sûr, vous devrez toujours modifier tous les appels à cette méthode pour utiliser la méthode Try. –

1

Une question assez vieux, mais je voudrais partager une approche qui pourrait aider. Nous utilisons également ce paradigme avec succès. Jetez un oeil sur le code ci-dessous. Ce que vous faites est en fait hériter de MethodLevelAspect et d'utiliser "Conseils".

OnException n'aide pas args.Proceed() à lancer une erreur. Donc, nous avons utilisé un bloc try-catch supplémentaire directement dans 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. 
    } 
} 
Questions connexes