Pergunta

Nunca fiquei completamente satisfeito com a forma como o tratamento de exceções funciona, há muitas exceções e o try/catch traz para a mesa (desenrolamento da pilha, etc.), mas parece quebrar muito o modelo OO no processo.

De qualquer forma, aqui está o problema:

Digamos que você tenha alguma classe que agrupa ou inclui operações de E/S de arquivos em rede (por exemplo,lendo e gravando em algum arquivo em algum caminho UNC específico em algum lugar).Por vários motivos, você não deseja que essas operações de IO falhem; portanto, se você detectar que elas falham, tente-as novamente e continue tentando-as novamente até que sejam bem-sucedidas ou atinja o tempo limite.Já tenho uma classe RetryTimer conveniente que posso instanciar e usar para suspender o thread atual entre novas tentativas e determinar quando o período de tempo limite expirou, etc.

O problema é que você tem várias operações de IO em vários métodos dessa classe e precisa agrupar cada uma delas na lógica try-catch/retry.

Aqui está um exemplo de trecho de código:

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

Então, como evitar a duplicação da maior parte desse código para cada operação de E/S de arquivo em toda a classe?Minha solução foi usar blocos delegados anônimos e um único método na classe que executou o bloco delegado passado para ele.Isso me permitiu fazer coisas assim em outros métodos:

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

Gosto um pouco disso, mas deixa muito a desejar.Eu gostaria de saber como outras pessoas resolveriam esse tipo de problema.

Foi útil?

Solução

Esta parece ser uma excelente oportunidade para dar uma olhada na Programação Orientada a Aspectos.Aqui está um bom artigo sobre AOP em .NET.A ideia geral é que você extraia a preocupação multifuncional (ou seja,Tente novamente por x horas) em uma classe separada e então você anotaria quaisquer métodos que precisassem modificar seu comportamento dessa maneira.Veja como pode parecer (com um bom método de extensão no Int32)

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

Outras dicas

Só me perguntando: o que você acha que seu método deixa a desejar?Você poderia substituir o delegado anônimo por um ..nomeado?delegado, algo como

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

Você também pode usar uma abordagem mais OO:

  • Crie uma classe base que faça o tratamento de erros e chame um método abstrato para realizar o trabalho concreto.(Padrão de método de modelo)
  • Crie classes concretas para cada operação.

Isto tem a vantagem de nomear cada tipo de operação executada e fornece um padrão de comando - as operações foram representadas como objetos.

Aqui está o que fiz recentemente.Provavelmente já foi feito melhor em outro lugar, mas parece bastante limpo e reutilizável.

Eu tenho um método utilitário parecido com este:

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

Então, em meu aplicativo, uso a sintaxe de expressão Lamda do c# para manter as coisas organizadas:

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

Isso chama meu método e tenta novamente até 5 vezes.Na quinta tentativa, a exceção original é lançada novamente dentro de uma exceção de nova tentativa.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top