2012-01-18 16 views
9

Tengo una acción MVC de larga duración (4-10 segundos) que ejecuta un informe de una llamada AJAX. Mientras se ejecuta, los usuarios pueden cambiar los parámetros y ejecutar otra cosa, por lo que cancelo la solicitud de AJAX antes de hacer otra.¿Cómo se cancela un lado del cliente de acción MVC de larga ejecución de AJAX (en javascript)?

Así, por ejemplo (ejemplo de jQuery, pero el problema ocurre independientemente):

// If we have an active request and it's not complete 
if(dataRequest && dataRequest.readyState != 'complete') 
{ 
    dataRequest.abort(); 
} 

dataRequest = $.ajax(...); 

lado del cliente esto parece funcionar bien, pero la petición cancelada aún se está ejecutando en el servidor. Por ejemplo, si el informe demora 10 segundos, y cancelo uno y comienzo el otro, la segunda solicitud demora 20 segundos.

creo que esto es debido a la session level locking:

[Si] dos solicitudes simultáneas se realizan para la misma sesión (utilizando el mismo valor SessionID), la primera solicitud obtiene acceso exclusivo a la información de la sesión. La segunda solicitud se ejecuta solo después de que finaliza la primera solicitud .

Por lo que la segunda solicitud no puede acceder a la sesión hasta que termine la primera. El uso de acciones MVC asíncronas no parece evitar esto, ya que la acción aún necesita poder realizar cambios en la sesión.

¿Es posible detener una acción y comenzar la siguiente sin usar AsyncController o [SessionState(SessionStateBehavior.ReadOnly)]?

Si no es necesario?

Respuesta

4

Como resulta que la respuesta completa requiere dos cosas:

primer lugar (gracias Darin para confirming it) cierra la sesión en esencia las páginas que se ejecutan uno tras otro. Further investigation ha señalado que este es un problema más general con las sesiones de ASP.Net, simplemente no pueden manejar la concurrencia optimista.

En segundo lugar, la solicitud cancelada debe comprobar si el cliente todavía está conectado. Hay situaciones en las que es posible que desee continuar (como un incendio y olvidarse de la acción ASP) pero en este caso, si el cliente ya no espera el informe, no tiene sentido continuar procesándolo.

Esto está disponible como un property of the response: this.Response.IsClientConnected

Así que para cancelar mi acción efectiva del lado del servidor cuando jQuery solicitado que tuve que escribir mi propio gestor de sesiones y añadir controles periódicos contra this.Response.IsClientConnected.

+1

Tenga cuidado al depender de 'IsClientConnected', ya que aparentemente puede ser un problema de rendimiento serio para verificarlo, consulte http://stackoverflow.com/questions/9094735/when-is-response-isclientconnected-slow – Keith

+0

¡Hola, Keith! Tengo un problema similar pero no pude resolverlo incluso con su respuesta, (http://stackoverflow.com/questions/29235850/how-to-abort-long-running-ajax-request-in-net-mvc-4) ¿Hay alguna forma de ayudarme? O podría publicar un código de muestra suyo para activar 'this.Response.IsClientConnected' –

4

[SessionState(SessionStateBehavior.ReadOnly)] o la sesión de deshabilitación completa es suficiente para permitir el acceso simultáneo a las acciones del controlador desde la misma sesión. Por lo tanto, la segunda solicitud (de la misma sesión) no necesita esperar a que la primera se complete antes de procesarse. En cuanto a cancelar la primera acción en el servidor, bueno, eso será más difícil. Deberá asociar a cada tarea una ID única y cuando se inicie una nueva tarea, devuelva esta identificación de tarea al cliente. Luego, cuando cancele la solicitud AJAX en el cliente llamando al .abort(), puede iniciar otra solicitud AJAX a otra acción del controlador y pasarle el ID de la tarea única. Esta acción del controlador establecerá una bandera común para indicar la primera acción para detener.

El TPL tiene un soporte de cancelación incorporado que puede echar un vistazo para simplificar eso con el fin de evitar una estructura de datos compartida entre los 2 que los sincronizarán.

+0

Ya estoy usando el TPL para hacer una gran parte del trabajo que se está tomando el tiempo: estoy ejecutando aproximadamente 25s de código consecutivo de larga duración en paralelo de aproximadamente 4-10. Sin embargo, usarlo para manejar el aborto parece reinventar la rueda; ¿por qué la nueva solicitud necesita decirle a la anterior (aún en ejecución) que aborte cuando ya se canceló? – Keith

+1

@Keith, no es la nueva solicitud lo que le dirá al viejo que aborte. Debe enviar una nueva solicitud especial de AJAX para indicar al servidor que interrumpa el procesamiento de una tarea en particular. Cuando haces 'dataRequest.abort();' solo estás abortando la solicitud de AJAX, pero si deseas abortar el procesamiento del lado del servidor, necesitas hacer mucho más trabajo que eso. –

+0

Hmm, ¿entonces IIS no sabe que la solicitud se ha cancelado en el nivel de transporte? Hubiera esperado que el hilo de la página se cancelara en el nivel IIS/ASP cuando la conexión en espera termina en el nivel de transporte. – Keith

1

.abort() es solo la cancelación del lado del cliente. Si desea supervisarlo desde el lado del servidor, una de las mejores maneras de hacerlo es buscar propiedad IsClientConnected, que le indicará cuál es el estado de la comunicación ajax.

+1

Consulte la respuesta aceptada y la pregunta original. Esta respuesta no agrega nada no especificado. También tenga mucho cuidado al usar 'IsClientConnected', ya que aparentemente puede ser un problema de rendimiento serio para comprobarlo, consulte http://stackoverflow.com/questions/9094735/when-is-response-isclientconnected-slow (aparentemente puede costar hasta ½s para verificarlo, pero nadie ha podido confirmar) – Keith

+0

Tiene razón, leí su publicación y tengo que revisar mi solución. – Dave

Cuestiones relacionadas