Invocando páginas ASP.NET con procesos pesados y no morir en el intento

Hola!

Hoy voy a escribir un artículo sobre procesamiento asíncrono en ASP.NET. El escenario planteado es el siguiente: Tenemos una aplicación ASP.NET desde la que necesitamos lanzar un proceso bastante pesado. Es decir, que llevará bastante tiempo terminarlo. Obviamente, si lo invocamos de la manera tradicional, es muy probable que recibamos un error de Timeout en el servidor.

Cuando se presenta una situación como la que describimos, lo primero que se suele venir a la cabeza es lo de siempre: usar hilos. Pero he aquí que estamos desarrollando bajo ASP.NET, en el que los objetos que se crean desde una página se destruyen cuando esa página se termina de procesar, para volver a ser creados en la siguiente petición. Esto quiere decir que si lanzamos un hilo para ejecutar un proceso pesado, hemos de dejarlo corriendo (sin esperar a que termine) para no bloquear el procesamiento de la página si no queremos recibir el error de Timeout.

Esta situación además implica que si queremos saber en qué estado se encuentra nuestro proceso, debemos de ser capaces de consultar el estado desde un objeto diferente (y probablemente desde un hilo diferente también) a aquél que lo haya iniciado.

También hemos de tener en cuenta que se pueden dar varios de estos procesos de forma simultánea, con lo que si queremos consultar el estado de la operación, hemos de identificar de alguna manera a qué operación nos estamos refiriendo.

En este caso, una posible solución sería la siguiente:

Lo primero, podemos crearnos una clase que encapsule el procesamiento pesado que hemos de realizar. Esta clase debe proveer los métodos necesarios para consultar el estado actual del proceso que estamos lanzando. Las ventajas de encapsularlo en una clase es que para lanzar el proceso, podremos crear una instancia de esa clase, dar valores a las propiedades que sean necesarias como datos de entrada del proceso, y arrancar en un hilo aparte el procesamiento llamando a un método de la instancia que acabamos de crear. Si es necesario podemos implementar el interfaz IDisposable si tenemos que liberar recursos al finalizar el proceso.

Lo segundo, podemos crear una clase que haga de repositorio para todos los procesos que se están ejecutando. Para identificar cada una de las instancias de la clase comentada en el apartado anterior, podríamos utilizar un Guid que generaremos al lanzar el proceso. Si estamos utilizando .NET 2.0, podríamos utilizar un Dictionary genérico, con clave de tipo Guid, y usando como tipo del valor la clase comentada en el apartado anterior. Si estamos usando .NET 1.0 ó 1.1, podremos utilizar un Hashtable. Esta clase deberá implementar el patrón Singleton, de manera que podamos acceder a la misma instancia desde la página que queramos, y además implementará los métodos necesarios para facilitarnos la interacción con la colección.

Por último, en la página que inicia el proceso, crearemos una instancia de nuestra clase que encapsula el procedimiento, y generaremos el Guid que lo indentificará en la clase repositorio. Añadimos la instancia al repositorio utilizando dicho Guid, y arrancamos un hilo pasándole el método que hace el trabajo pesado como punto de arranque del hilo.

Después podemos redirigir la respuesta a otra página donde se comprobará el estado de la petición, pasándole a esta página el Guid que hemos generado. O también se podría redirigir a una página que muestre el listado de peticiones pendientes.

Si no estamos usando Ajax, podemos hacer que la página que comprueba el estado del proceso se refresque (si no ha terminado aún) añadiendo una cabecera a la respuesta de la siguiente manera: Response.AddHeader(“Refresh”, “2”)

Si el proceso ha terminado, simplemente quitamos del repositorio la instancia que ha terminado su ejecución (utilizando nuevamente el Guid) – y si implementa el interfaz IDisposable, llamar al método Dispose – y redirigimos a una página que indique que ya hemos terminado la ejecución del proceso.

Bueno, pues esto es todo por hoy… Saludos!

Technorati tags: ,

4 comentarios en “Invocando páginas ASP.NET con procesos pesados y no morir en el intento”

  1. Hola Buenos Dias.

    Tengo un pequeño problema con el uso del ApartmentState.
    Tengo una pagina con un boton el cual manda a ejecutar a una dll la cual abre una ventana. Mi problema es que al utilizar ApartmentState.MTA al dar click en el boton abre la ventana en segundo plano o minimizada y si lo cambio a ApartmentState.STA lo hace bien pero las imagenes no las muestra. De que forma tengo que usar el ApartmentState.MTA para que al dar click en el boton abra la ventana en primer plano para poder trabajar sobre ella.

    Saludos y gracias

  2. Hola Ana,

    Si es una aplicación Web, tienes que repensar la estructura de la aplicación. Una dll de negocio nunca debería abrir una ventana. Otra cosa es que proporcione la imagen a la capa de presentación, que se encargue de mostrarla al usuario…

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *