2010-05-28 10 views
40

He estado tratando de crear un controlador en mi proyecto para entregar lo que podrían llegar a ser informes bastante complejos. Como resultado, pueden tomar un tiempo relativamente largo y una barra de progreso ciertamente ayudaría a los usuarios a saber que las cosas están progresando. El informe se iniciará a través de una solicitud de AJAX, con la idea de que las solicitudes JSON periódicas obtendrán el estado y actualizarán la barra de progreso.Necesita un proceso de larga ejecución de ASP.NET MVC con comentarios de los usuarios

He estado experimentando con AsyncController, ya que parece ser una buena forma de ejecutar procesos largos sin atascar recursos, pero no parece que tenga ninguna forma de verificar el progreso (y parece bloquear otras solicitudes JSON y todavía no he descubierto por qué). Después de eso, intenté recurrir a almacenar el progreso en una variable estática en el controlador y leer el estado de eso, ¡pero para ser sincero, todo parece un poco hacky!

¡Todas las sugerencias fueron aceptadas con gratitud!

+0

¿Cuál es el resultado del informe (pantalla, pdf?) –

+0

Pantalla, ya sea un fragmento de HTML o un objeto JSON que no me importa tanto en este momento (siempre que obtenga una respuesta que pueda análisis y visualización). – Jason

+1

¿No sería este un gran candidato para ASP.Net SignalR? Sé que este es un hilo viejo, pero ahora estoy en una situación similar. –

Respuesta

43

He aquí una muestra escribí que usted podría intentar:

controlador:

public class HomeController : AsyncController 
{ 
    public ActionResult Index() 
    { 
     return View(); 
    } 

    public void SomeTaskAsync(int id) 
    { 
     AsyncManager.OutstandingOperations.Increment(); 
     Task.Factory.StartNew(taskId => 
     { 
      for (int i = 0; i < 100; i++) 
      { 
       Thread.Sleep(200); 
       HttpContext.Application["task" + taskId] = i; 
      } 
      var result = "result"; 
      AsyncManager.OutstandingOperations.Decrement(); 
      AsyncManager.Parameters["result"] = result; 
      return result; 
     }, id); 
    } 

    public ActionResult SomeTaskCompleted(string result) 
    { 
     return Content(result, "text/plain"); 
    } 

    public ActionResult SomeTaskProgress(int id) 
    { 
     return Json(new 
     { 
      Progress = HttpContext.Application["task" + id] 
     }, JsonRequestBehavior.AllowGet); 
    } 
} 

Vista:

<script type="text/javascript"> 
$(function() { 
    var taskId = 543; 
    $.get('/home/sometask', { id: taskId }, function (result) { 
     window.clearInterval(intervalId); 
     $('#result').html(result); 
    }); 

    var intervalId = window.setInterval(function() { 
     $.getJSON('/home/sometaskprogress', { id: taskId }, function (json) { 
      $('#progress').html(json.Progress + '%'); 
     }); 
    }, 5000); 
}); 
</script> 

<div id="progress"></div> 
<div id="result"></div> 

La idea es iniciar una operación asincrónica que informará del progreso a través de HttpContext.Application, lo que significa que cada tarea debe tener una identificación única. Luego, en el lado del cliente, comenzamos la tarea y luego enviamos múltiples solicitudes de AJAX (cada 5 segundos) para actualizar el progreso. Puede ajustar los parámetros para ajustarse a su escenario. Una mejora adicional sería agregar manejo de excepciones.

+0

Gracias, Darin, probé tu código pero igual recibí todas las solicitudes 'copiando' una detrás de otra. Después de haber pasado esta mañana rastreando lentamente por una respuesta, ahora estoy empezando a pensar que esto podría estar relacionado con la sesión que no permite múltiples solicitudes simultáneamente, obligando a las solicitudes a ser sincrónicas. Un cambio que tuve que hacer fue su uso de Task.Factory ya que actualmente estoy trabajando en .NET 3, no 4. ¿Cree que Task.Factory resolvería el problema de bloqueo de sesión? – Jason

+1

Además, acabo de ejecutar su código y lo hice eliminando cualquier código en mi controlador que haga referencia a la sesión, así que obviamente ese es mi problema. Lamentablemente, todos mis controladores requieren varios bits de datos que tengo en la sesión (y que recupero dentro de un controlador de base que se extienden), por lo que no veo ninguna forma sencilla de resolver ese problema. – Jason

+0

La sesión no siempre está disponible en acciones asíncronas. –

2

4.5 años después de que esta pregunta ha sido respondida, y tenemos una biblioteca que puede hacer esta tarea mucho más fácil: SignalR. No es necesario usar el estado compartido (lo cual es malo porque puede generar resultados inesperados), solo use la clase HubContext para conectarse a un concentrador que envía mensajes al cliente.

Primero, configuramos una conexión SignalR como de costumbre (consulte, por ejemplo, here), excepto que no necesitamos ningún método de servidor en nuestro Hub. Luego hacemos una llamada AJAX a nuestro Endpoint/Controller/whatever y pasamos el ID de conexión, que obtenemos como de costumbre: var connectionId = $.connection.hub.id;. En el lado del servidor, puede iniciar su proceso en un hilo diferente y volver a ejecutar 200 OK para el cliente. El proceso se conoce el ID de conexión de modo que pueda enviar mensajes de vuelta al cliente, así:

GlobalHost.ConnectionManager.GetHubContext<LogHub>() 
           .Clients.Client(connectionId) 
           .log(message); 

Aquí, log es un método de cliente que desea llamar desde el servidor, por lo que debe definirse como se suele hacer con SignalR:

$.connection.logHub.client.log = function(message){...}; 

Más detalles en mi blog here

+5

Por supuesto, hubo un comentario sobre SignalR publicado hace casi 2 años sobre la pregunta anterior. Realmente no deberías solo publicar un enlace a tu blog, ya que eso es spam en el límite y las respuestas de solo enlace están mal vistas. –

+0

Disculpe, no fue spam. Respuesta actualizada – ulu

+0

Esto parece una muy buena respuesta. ¿Alguien usa SignalR en lugar de sondear como en la respuesta de Darrin? – Jess

0

Si usted tiene un acceso a la máquina de servidor web como una opción puede crear ventanas de servicio o aplicación de consola sencilla de realizar lon g trabajo de ejecución. Su aplicación web debe agregar un registro a db para indicar que la operación debe comenzar, y su servicio de Windows que periódicamente verifica nuevos registros en db debería comenzar a realizar la tarea e informar el progreso a db. Su aplicación web puede usar una solicitud ajax para verificar el progreso y mostrarla a los usuarios.

Utilicé este método para implementar informes de Excel para la aplicación asp.net mvc. Este informe fue creado por el servicio de Windows que se ejecuta en la máquina y constantemente busca nuevos registros en la tabla de informes y cuando encuentra un nuevo registro, comienza a crear un informe e indica un progreso al actualizar el campo de registro. La aplicación Asp.net mvc acaba de agregar un nuevo registro de informe y rastrear el progreso en la base de datos hasta que termine y luego proporcionó un enlace para descargar el archivo listo.

Cuestiones relacionadas