2011-01-04 7 views
15

Tengo algunas acciones de utilidad que devuelven texto a través del return Content("my text","text/plain").Transmisión de salida de texto para acción prolongada?

A veces, estos métodos tardan unos minutos en ejecutarse (es decir, análisis de registro, mantenimiento de la base de datos).

Me gustaría modificar mi método de acción para que en lugar de devolver todo el resultado a la vez, el texto se transmita al cliente cuando esté listo.

Aquí está un ejemplo artificioso:

public ActionResult SlowText() 
{ 
    var sb = new System.Text.StringBuilder(); 
    sb.AppendLine("This happens quickly..."); 
    sb.AppendLine("Starting a slow 10 second process..."); 
    System.Threading.Thread.Sleep(10000); 
    sb.AppendLine("All done with 10 second process!"); 
    return Content(sb.ToString(), "text/plain"); 
} 

Como está escrito, esta acción volverá tres líneas de texto después de 10 segundos. Lo que quiero es una forma de mantener abierta la secuencia de respuesta y devolver las dos primeras líneas inmediatamente, y luego la tercera línea después de 10 segundos.

Recuerdo hacer esto hace más de 10 años en Classic ASP 3.0 usando el objeto Response. ¿Hay alguna forma oficial de MVC para lograr esto?

-

Actualización: el uso de la maquinilla de afeitar .cshtml en la aplicación; pero no usando ninguna vista (solo ContentResult) para estas acciones.

+1

¿Navaja o aspx? La diferencia es que el motor Razor no permite la salida de transmisión. – Buildstarted

+0

nos encontramos con el mismo problema y también utilizamos directamente Response.OutputStream en el controlador. Tengo curiosidad por saber si encuentras alguna solución? –

Respuesta

5

Escribir directamente en el objeto Response debería funcionar, pero solo en algunos casos simples. Muchas características de MVC dependen de la sustitución del escritor de salida (por ejemplo, vistas parciales, motor de visualización Razor y otros) y si escribe directamente en la Respuesta, su resultado estará fuera de servicio.

Sin embargo, si no utiliza una vista y en su lugar escribe directamente en el controlador, entonces debería estar bien (suponiendo que su acción no se haya iniciado como acción secundaria).

+0

Gracias por las compras. Me siento "sucio" manipulando 'Response' directamente dentro del controlador ... en su opinión, ¿debería escribir un' ActionScriptResultResult'ResultResult' y devolver eso? ¿O está bien meterse con el objeto Response dentro del controlador en algunas circunstancias especiales? Me encantaría la sensación de "Code Smell" de otra persona en esto. Gracias. – Portman

+1

Sí, si tuviera que hacerlo probablemente escribiría un nuevo 'StreamingContentResult' que tal vez aceptara un' Func' a través del constructor que representaría el trabajo que debía hacerse. – marcind

1

omitiría completamente el controlador MVC ya que de todos modos romperá la encapsulación. En su lugar usaría una implementación de IHttpHandler sin formato, transmitiendo directamente a la secuencia de salida antes mencionada.

1

Estás exponiéndote a un tiempo de espera del navegador si el proceso lleva más tiempo del previsto originalmente. Entonces no tiene una forma de recuperar lo sucedido/a menos que implemente un método separado que proporcione la información sobre el proceso de ejecución prolongada.

Dado que desea el otro método de todos modos, puede iniciar un proceso de larga ejecución y regresar inmediatamente. Haga que el navegador compruebe el otro método que proporciona la información más reciente sobre el proceso de larga ejecución. La última vez que tuve que hacer esto, lo mantuve simple y simplemente configuré el encabezado de actualización desde el controlador antes de devolver la vista.

En cuanto a iniciar un proceso de larga duración, se puede hacer algo como esto:

// in the controller class 
delegate void MyLongProcess(); 
//... 
// in the method that starts the action 
MyLongProcess processTask = new MyLongProcess(_someInstance.TheLongRunningImplementation); 
processTask.BeginInvoke(new AsyncCallback(EndMyLongProcess), processTask); 
//... 
public void EndMyLongProcess(IAsyncResult result) 
{ 
    try{ 
     MyLongProcess processTask = (MyLongProcess)result.AsyncState; 
     processTask.EndInvoke(result); 
     // anything you needed at the end of the process 
    } catch(Exception ex) { 
     // an error happened, make sure to log this 
     // as it won't hit the global.asax error handler      
    } 
} 

En cuanto a ¿Dónde poner el registro de las acciones que sucedieron, le toca a usted a cuánto tiempo vivido desea es ser. Puede ser tan simple como un campo/clase estático donde agrega la información del proceso en curso, o en su lugar, la guarda en un almacén de datos donde puede sobrevivir a un reciclaje de aplicaciones.

Lo anterior supone que se trata de un proceso de larga ejecución que continúa informando las acciones que se han llevado a cabo.La transmisión por secuencias es un tema diferente, pero lo anterior aún puede desempeñar un papel al mantener las operaciones en su controlador & solo la pieza responsable de la transmisión de lo que esté disponible para el cliente en el resultado de la acción.

0

Pruebe Response.Flush y BufferOutput a false. Tenga en cuenta que funcionaría con los diferentes resultados de acción, debe escribir directamente en el objeto response. Probablemente puede usarlo junto con AsyncController.

1

Puede implementar su ActionResult personalizado como ContentStreamingResult y usar HttpContext, HttpRequest y HttpResponse en el método ExecuteResult.

public class ContentStreamingResult : ActionResult 
    { 
     private readonly TextReader _reader; 

     public ContentStreamingResult(TextReader reader) 
     { 
      _reader = reader; 
     } 

     public override void ExecuteResult(ControllerContext context) 
     { 
      var httpContext = context.HttpContext; 
      //Read text from the reader and write to the response 
     } 
    } 

public class YourController : Controller 
    { 
     public ContentStreamingResult DownloadText() 
     { 
      string text = "text text text"; 
      return new ContentStreamingResult(new System.IO.StringReader(text)); 
     } 
    } 
Cuestiones relacionadas