Pregunta

Nunca he sido completamente feliz con la forma en que el manejo de excepciones obras, hay un montón de excepciones y try/catch trae a la mesa (el desenredo de pila, etc.), pero parece romper muchas de las OO modelo en el proceso.

De todos modos, aquí está el problema:

Digamos que usted tiene algunos de la clase que contiene o incluye operaciones de e / s de archivos de red (por ejemplo,la lectura y la escritura a algún archivo en particular ruta de acceso UNC en algún lugar).Por diversas razones, usted no desea que las operaciones de e / s a fallar, así que si usted detecta que no se reintentar y intentándolo hasta que el éxito o el llegar a un tiempo de espera.Ya tengo un conveniente RetryTimer de la clase a la que puedo crear y utilizar a dormir el subproceso actual entre reintentos y determinar cuando el periodo de tiempo que ha transcurrido, etc.

El problema es que usted tiene un montón de operaciones de e / s en varios de los métodos de esta clase, y que usted necesita para envolver cada uno de ellos en try-catch / lógica de reintento.

He aquí un ejemplo de fragmento 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();
    }
}

Así que, ¿cómo evitar la duplicación de la mayoría de este código para cada tipo de archivo operación de e / s durante la clase?Mi solución fue usar anónimos, delegado de bloques y un único método en la clase que ejecuta el delegado de bloque que se ha pasado.Esto me permitió hacer cosas como esta en otros métodos:

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

Me gusta un poco, pero deja mucho que desear.Me gustaría saber cómo otras personas para resolver este tipo de problema.

¿Fue útil?

Solución

Esto se ve como una excelente oportunidad para tener un vistazo a la Programación Orientada a Aspectos.Aquí hay un buen artículo sobre AOP en .NET.La idea general es que a usted le extracto de la cruz-funcional de la preocupación (es decir,Reintento de x horas) en una clase independiente y, a continuación, te gustaría anotar cualquiera de los métodos que se necesitan para modificar el comportamiento de esa manera.He aquí lo que podría parecer (con un buen método de extensión en Int32)

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

Otros consejos

Sólo me preguntaba, ¿cuál cree que su método deja que desear?Usted puede reemplazar la anónimos, delegado con un..nombrado?delegado, algo así 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" );
    }

También se puede utilizar un enfoque más ORIENTADO:

  • Crear una clase base que hace que el manejo de errores y llama a un método abstracto para realizar el trabajo concreto.(Método de plantilla de patrón)
  • Crear clases concretas para cada operación.

Esto tiene la ventaja de que se nombre a cada tipo de operación a realizar y le da un patrón Comando de operaciones han sido representados como objetos.

Esto es lo que hice recientemente.Probablemente ha sido realizada en otro lugar mejor, pero parece bastante limpio y reutilizable.

Tengo un método de utilidad que tiene este aspecto:

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

Luego, en mi solicitud, yo uso c#'s Lamda la sintaxis de expresión para mantener las cosas en orden:

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

Esto llama mi método y reintentos hasta 5 veces.En el quinto intento, el original de la excepción se vuelve a producir en el interior de un reintento de excepción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top