2011-01-04 3 views
15

Tôi có một vài hành động tiện ích trả lại kết quả đầu ra văn bản qua return Content("my text","text/plain").Truyền văn bản đầu ra cho hành động dài hạn?

Đôi khi, các phương pháp này mất vài phút để chạy (tức là phân tích nhật ký, bảo trì cơ sở dữ liệu).

Tôi muốn sửa đổi phương pháp hành động của mình để thay vì trả lại tất cả đầu ra cùng một lúc, văn bản thay vào đó được truyền trực tiếp tới ứng dụng khi nó sẵn sàng.

Dưới đây là một ví dụ contrived:

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

Theo văn bản, hành động này sẽ trở lại ba dòng văn bản sau 10 giây. Những gì tôi muốn là một cách để giữ cho dòng phản hồi mở, và trả về hai dòng đầu tiên ngay lập tức, và sau đó là dòng thứ ba sau 10 giây.

Tôi nhớ đã thực hiện việc này 10+ năm trước trong ASP 3.0 cổ điển bằng cách sử dụng đối tượng Phản hồi. Có cách nào chính thức, thân thiện với MVC để thực hiện việc này không?

-

Cập nhật: sử dụng Razor .cshtml trong ứng dụng; nhưng không sử dụng bất kỳ chế độ xem nào (chỉ ContentResult) cho những hành động này.

+1

Dao cạo hoặc aspx? Sự khác biệt là công cụ Razor không cho phép phát trực tuyến. – Buildstarted

+0

chúng tôi chạy vào cùng một vấn đề và chúng tôi cũng trực tiếp sử dụng Response.OutputStream trong bộ điều khiển. Tôi tò mò muốn biết nếu bạn tìm thấy bất kỳ giải pháp? –

Trả lời

5

Viết trực tiếp vào đối tượng Response sẽ hoạt động nhưng chỉ trong một số trường hợp đơn giản. Nhiều tính năng MVC phụ thuộc vào sự thay thế của nhà văn đầu ra (ví dụ: một phần lượt xem, công cụ xem Dao cạo và các công cụ khác) và nếu bạn viết trực tiếp vào Phản hồi, kết quả của bạn sẽ không đúng thứ tự.

Tuy nhiên, nếu bạn không sử dụng chế độ xem và thay vào đó viết thẳng vào bộ điều khiển thì bạn nên ổn (giả sử hành động của bạn không được gọi là hành động con).

+0

Cảm ơn vì gotchas. Tôi cảm thấy "bẩn" thao tác 'Response' trực tiếp bên trong Controller ... theo ý kiến ​​của bạn, tôi có nên viết một ActionResult' StreamingContentResult' và trả về nó không? Hoặc là nó tốt để gây rối với đối tượng Response bên trong bộ điều khiển trong một số trường hợp đặc biệt. Sẽ yêu cảm giác của người khác về "Code Smell" về điều này. Cảm ơn. – Portman

+1

Có, nếu tôi đã làm điều đó tôi có lẽ sẽ viết một 'StreamingContentResult' mới có thể chấp nhận một' Func' thông qua các nhà xây dựng mà sẽ đại diện cho công việc cần được thực hiện. – marcind

1

Tôi sẽ bỏ qua bộ điều khiển MVC hoàn toàn vì bạn sẽ ngắt đóng gói. Ở nơi đó tôi sẽ sử dụng một triển khai IHttpHandler được barenaked, truyền trực tiếp đến luồng đầu ra nói trên.

1

Bạn đang tự phơi mình với thời gian chờ của trình duyệt nếu quá trình này mất nhiều thời gian hơn dự định ban đầu. Sau đó, bạn không có một cách để khôi phục những gì đã xảy ra/trừ khi bạn thực hiện một phương pháp riêng biệt cung cấp thông tin về quá trình chạy dài.

Cho dù bạn vẫn muốn phương thức khác, bạn có thể bắt đầu quá trình chạy dài và quay lại ngay lập tức. Yêu cầu trình duyệt kiểm tra phương thức khác cung cấp thông tin mới nhất về quy trình chạy dài. Vào lần cuối cùng tôi phải làm điều này, tôi giữ nó đơn giản và chỉ cần đặt tiêu đề làm mới từ bộ điều khiển trước khi trả lại chế độ xem.

Như để bắt đầu một quá trình dài chạy, bạn có thể làm một cái gì đó như thế này:

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

Đối với nơi nào bạn đặt các bản ghi của các hành động đã xảy ra, đó là tùy thuộc vào bạn để bao lâu sống bạn muốn nó được. Nó có thể đơn giản như một trường/lớp tĩnh nơi bạn thêm thông tin của quá trình đang diễn ra, hoặc thay vào đó lưu nó vào một kho dữ liệu, nơi nó có thể tồn tại một ứng dụng tái chế.

Giả định ở trên đây là tất cả về một quy trình chạy dài liên tục báo cáo các hành động đã được thực hiện.Streaming là một chủ đề khác, nhưng ở trên vẫn có thể đóng một vai trò trong việc giữ các hoạt động trong bộ điều khiển của bạn & chỉ phần chịu trách nhiệm phát trực tuyến những gì có sẵn cho khách hàng trong kết quả hành động.

0

Hãy thử Response.FlushBufferOutput đến false. Lưu ý rằng nó sẽ làm việc với các kết quả hành động khác nhau, bạn phải ghi trực tiếp vào đối tượng response. Có lẽ bạn có thể sử dụng nó với sự kết hợp với AsyncController.

1

Bạn có thể triển khai ActionResult tùy chỉnh của mình như ContentStreamingResult và sử dụng HttpContext, HttpRequest và HttpResponse trong phương thức 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)); 
     } 
    }