2011-01-04 2 views
15

У меня есть несколько полезных действий, которые возвращают вывод текста через return Content("my text","text/plain").Потоковый вывод текста для продолжительного действия?

Иногда эти методы занимают несколько минут для запуска (т. Е. Синтаксический анализ журнала, обслуживание базы данных).

Я хотел бы изменить свой метод действий, чтобы вместо того, чтобы сразу возвращать весь вывод, текст будет передан клиенту, когда он будет готов.

Вот надуманный пример:

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

Как написано, это действие будет возвращать три строки текста через 10 секунд. То, что я хочу, это способ сохранить поток ответа открытым и сразу же вернуть первые две строки, а затем третью строку через 10 секунд.

Я помню, как это делал 10 лет назад в классическом ASP 3.0 с использованием объекта Response. Есть ли официальный способ MVC для этого?

-

Update: с помощью Razor .cshtml в приложении; но не используя какие-либо представления (только ContentResult) для этих действий.

+1

Razor or aspx? Разница в том, что механизм Razor не позволяет выводить поток. – Buildstarted

+0

мы сталкиваемся с той же проблемой, и мы также непосредственно использовали Response.OutputStream в контроллере. Мне интересно узнать, нашли ли вы какие-либо решения? –

ответ

5

Письмо непосредственно на объект Response должно работать, но только в некоторых простых случаях. Многие функции MVC зависят от замены замещения вывода (например, частичные представления, механизм просмотра Razor и т. Д.), И если вы будете писать непосредственно в ответ, ваш результат будет неработоспособным.

Однако, если вы не используете представление и вместо этого пишете прямо в контроллере, тогда вы должны быть в порядке (если ваше действие не вызывается дочерним действием).

+0

Спасибо за gotchas. Я чувствую «грязную» манипуляцию «Ответ» непосредственно внутри Контроллера ... по-вашему, должен ли я написать ActionResult «StreamingContentResult» и вернуть его? Или это прекрасно, чтобы вступить в конфликт с объектом Response внутри контроллера в некоторых особых обстоятельствах. Будет любить чужое чувство «Code Smell» на этом. Благодарю. – Portman

+1

Да, если бы я это сделал, я бы, вероятно, написал новый «StreamingContentResult», который, возможно, принял «Func» через конструктор, который будет представлять работу, которая должна быть выполнена. – marcind

1

Я бы пропустил контроллер MVC полностью, так как вы все равно сломаете инкапсуляцию. В этом месте я бы использовал реализацию Barenaked IHttpHandler, потоковое прямое отношение к вышеупомянутому потоку вывода.

1

Вы подвергаетесь таймауту браузера, если процесс занимает больше времени, чем первоначально предполагалось. Тогда у вас нет способа восстановить то, что произошло/если вы не реализуете отдельный метод, который дает информацию о длительном процессе.

Учитывая, что вы хотите другой метод в любом случае, вы можете начать длительный процесс и немедленно вернуться. Попросите браузер проверить другой метод, который дает самую последнюю информацию о продолжительном процессе. В последний раз я должен был сделать это, я оставил его простым и просто установил заголовок обновления из контроллера, прежде чем возвращать представление.

Что касается запуска процесса долго работает, вы можете сделать что-то вроде этого:

// 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      
    } 
} 

Как для того, где вы положили журнал действий, которые произошли, это до вас, как долго жили вы хотите это будет. Он может быть таким же простым, как статическое поле/класс, в котором вы добавляете информацию о текущем процессе или вместо этого сохраняете его в хранилище данных, где он может пережить повторное использование приложения.

Вышеупомянутое предполагает, что все это связано с длительным процессом, который продолжает сообщать о действиях, которые были выполнены.Потоковая передача - это другой вопрос, но вышеупомянутое может по-прежнему играть роль в поддержании операций в вашем контроллере & только части, ответственной за потоковое вещание, доступное клиенту в результате действия.

0

Пробег: Response.Flush и BufferOutput - false. Обратите внимание, что это будет работать с различными результатами действий, вы должны напрямую писать в объект response. Вероятно, вы можете использовать его вместе с AsyncController.

1

Вы можете реализовать свой собственный ActionResult, например ContentStreamingResult, и использовать HttpContext, HttpRequest и HttpResponse в методе 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)); 
     } 
    } 
Смежные вопросы