Question

Je n'ai jamais été complètement satisfait de la façon dont fonctionne la gestion des exceptions, il y a beaucoup d'exceptions et try/catch apporte à la table (déroulement de la pile, etc.), mais cela semble casser une grande partie du modèle OO dans le processus.

Quoi qu'il en soit, voici le problème :

Disons que vous disposez d'une classe qui encapsule ou inclut des opérations d'E/S sur les fichiers en réseau (par ex.lire et écrire dans un fichier sur un chemin UNC particulier quelque part).Pour diverses raisons, vous ne voulez pas que ces opérations d'E/S échouent, donc si vous détectez qu'elles échouent, vous les réessayez et vous continuez à les réessayer jusqu'à ce qu'elles réussissent ou que vous atteigniez un délai d'attente.J'ai déjà une classe RetryTimer pratique que je peux instancier et utiliser pour mettre en veille le thread en cours entre les tentatives et déterminer quand le délai d'attente est écoulé, etc.

Le problème est que vous avez un tas d'opérations d'E/S dans plusieurs méthodes de cette classe et que vous devez envelopper chacune d'elles dans une logique try-catch/retry.

Voici un exemple d'extrait de code :

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
bool success = false;
while (!success)
{
    try
    {
        // do some file IO which may succeed or fail
        success = true;
    }
    catch (IOException e)
    {
        if (fileIORetryTimer.HasExceededRetryTimeout)
        {
            throw e;
        }
        fileIORetryTimer.SleepUntilNextRetry();
    }
}

Alors, comment éviter de dupliquer la majeure partie de ce code pour chaque opération d’E/S de fichier dans toute la classe ?Ma solution consistait à utiliser des blocs délégués anonymes et une seule méthode dans la classe qui exécutait le bloc délégué qui lui était transmis.Cela m'a permis de faire des choses comme ceci avec d'autres méthodes :

this.RetryFileIO( delegate()
    {
        // some code block
    } );

J'aime un peu ça, mais cela laisse beaucoup à désirer.J'aimerais savoir comment d'autres personnes résoudraient ce genre de problème.

Était-ce utile?

La solution

Cela semble être une excellente occasion de jeter un œil à la programmation orientée aspect.Voici un bon article sur AOP dans .NET.L'idée générale est que vous extrayiez la préoccupation interfonctionnelle (c'est-à-direRéessayez pendant x heures) dans une classe distincte, puis vous annoterez toutes les méthodes qui doivent modifier leur comportement de cette manière.Voici à quoi cela pourrait ressembler (avec une belle méthode d'extension sur Int32)

[RetryFor( 10.Hours() )]
public void DeleteArchive()
{
  //.. code to just delete the archive
}

Autres conseils

Je me demandais simplement si, selon vous, votre méthode laisse à désirer ?Vous pouvez remplacer le délégué anonyme par un ..nommé?délégué, quelque chose comme

    public delegate void IoOperation(params string[] parameters);

    public void FileDeleteOperation(params string[] fileName)
    {
        File.Delete(fileName[0]);
    }

    public void FileCopyOperation(params string[] fileNames)
    {
        File.Copy(fileNames[0], fileNames[1]);
    }

    public void RetryFileIO(IoOperation operation, params string[] parameters)
    {
        RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10));
        bool success = false;
        while (!success)
        {
            try
            {
                operation(parameters);
                success = true;
            }
            catch (IOException e)
            {
                if (fileIORetryTimer.HasExceededRetryTimeout)
                {
                    throw;
                }
                fileIORetryTimer.SleepUntilNextRetry();
            }
        }
    }

    public void Foo()
    {
        this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete" );
        this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination" );
    }

Vous pouvez également utiliser une approche plus OO :

  • Créez une classe de base qui effectue la gestion des erreurs et appelle une méthode abstraite pour effectuer le travail concret.(Modèle de méthode modèle)
  • Créez des classes concrètes pour chaque opération.

Cela présente l'avantage de nommer chaque type d'opération que vous effectuez et vous donne un modèle de commande - les opérations ont été représentées sous forme d'objets.

Voici ce que j'ai fait récemment.Cela a probablement été mieux fait ailleurs, mais cela semble assez propre et réutilisable.

J'ai une méthode utilitaire qui ressemble à ceci :

    public delegate void WorkMethod();

    static public void DoAndRetry(WorkMethod wm, int maxRetries)
    {
        int curRetries = 0;
        do
        {
            try
            {
                wm.Invoke();
                return;
            }
            catch (Exception e)
            {
                curRetries++;
                if (curRetries > maxRetries)
                {
                    throw new Exception("Maximum retries reached", e);
                }
            }
        } while (true);
    }

Ensuite, dans mon application, j'utilise la syntaxe d'expression Lamda de c# pour garder les choses en ordre :

Utility.DoAndRetry( () => ie.GoTo(url), 5);

Cela appelle ma méthode et réessaye jusqu'à 5 fois.À la cinquième tentative, l’exception d’origine est renvoyée à l’intérieur d’une exception de nouvelle tentative.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top