2012-05-08 83 views
5

顯然,我不理解如何使用ContinueWith方法。我的目標是執行任務,完成後返回消息。Task.ContinueWith執行順序

這裏是我的代碼:

public string UploadFile() 
    { 
     if (Request.Content.IsMimeMultipartContent()) 
     { 
      //Save file 
      MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files")); 
      Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider); 

      string filename = "Not set"; 

      task.ContinueWith(o => 
      { 
       //File name 
       filename = provider.BodyPartFileNames.First().Value; 
      }, TaskScheduler.FromCurrentSynchronizationContext()); 

      return filename; 
     } 
     else 
     { 
      return "Invalid."; 
     } 
    } 

變量 「文件名」 總是返回 「未設置」。看起來ContinueWith方法內的代碼永遠不會被調用。 (如果我在VS中逐行進行調試,它確實會被調用。)

在我的ASP.NET Web API控制器/ Ajax POST中調用此方法。

我在這裏做錯了什麼?

+2

這是因爲你正在做一個異步操作。 –

+0

另外,除了任務是異步的,我認爲他們甚至沒有開始。 – GolfWolf

回答

7

如果您使用的是異步操作,最好的方法是讓您的操作同步,否則您將失去正在進行的異步調用的優勢。嘗試重寫你的方法如下:

public Task<string> UploadFile() 
{ 
    if (Request.Content.IsMimeMultipartContent()) 
    { 
     //Save file 
     MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files")); 
     Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider); 

     return task.ContinueWith<string>(contents => 
     { 
      return provider.BodyPartFileNames.First().Value; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
    else 
    { 
     // For returning non-async stuff, use a TaskCompletionSource to avoid thread switches 
     TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(); 
     tcs.SetResult("Invalid."); 
     return tcs.Task; 
    } 
} 
+0

哇,謝謝!這對我有效。感謝所有其他人 - 感謝您的幫助。 – Rivka

+0

我有一個關於TaskScheduler.FromCurrentSynchronizationContext的問題 - 我的理解是這是一個真正的UI方法(與UI線程同步),但我想知道是否有可能在webapi方法中使用的任何原因。有沒有? – AlexGad

+0

同步上下文也可用於存儲一些線程局部變量,並確保在控件返回到繼續時重新填充它們。一個例子是'Thread.CurrentPrincipal'。如果我沒有記錯,那麼ASP.NET運行時也會爲這種情況定義一個同步上下文。 – carlosfigueira

1

您應該從方法中返回類型Task<T>,在這種情況下,它應該是Task<string>

+0

我試過這個,但我有同樣的問題 - 「文件名」沒有被設置。 – Rivka

0

您正在使用異步操作。如果你想等待它的完成,你必須以其它方式使用你的任務Wait方法:

task.ContinueWith(o => 
     { 
      //File name 
      filename = provider.BodyPartFileNames.First().Value; 
     ).Wait(); 

return filename; 

編輯: 一些非同步的方法,只要它是創建啓動任務,而其他問你明確啓動它們。您必須查閱每個文檔以確保。在這種情況下,任務似乎自動啓動。

+0

試過這個。它似乎沒有使用Wait()返回任何東西(甚至沒有「未設置」)。 – Rivka

+0

然後這意味着任務在創建時不會運行。我相應地編輯了我的答案。 – Falanwe

+0

獲取錯誤開始可能無法在具有空操作的任務上調用。任務的狀態是「RanToCompletion」。 – Rivka

2

了變量未設置的原因是:

  • 任務被實例化,但無法運行。
  • 即使任務運行,該功能可能會返回之前,他們完成運行,所以,它仍然會返回「未設置」。此修復正在等待最後的任務(一個設置fileName)完成。

你的代碼可以固定這樣的:

public string UploadFile() 
{ 
    if (Request.Content.IsMimeMultipartContent()) 
    { 
     //Save file 
     MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files")); 
     Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider); 

     string filename = "Not set"; 

     var finalTask = task.ContinueWith(o => 
      { 
       //File name 
       filename = provider.BodyPartFileNames.First().Value; 
      }, TaskScheduler.FromCurrentSynchronizationContext()); 

     task.Start(); 

     finalTask.Wait(); 

     return filename; 
    } 
    else 
    { 
     return "Invalid."; 
    } 
} 

的增加有以下幾種:

  • 分配的task.ContinueWith返回值到一個名爲finalTask變量。我們需要這個任務,因爲我們將等待它完成
  • 開始任務(task.Start();線)
  • 等待最後的任務完成返回(finalTask.Wait();

如果可能的話之前,請考慮不要異步實現,因爲最終它是同步的(你正在等待它完成),而且當前的實現增加了可能可以避免的複雜性。

考慮沿着這些線路做一些事情(如果可能):

public string UploadFile() 
{ 
    if (Request.Content.IsMimeMultipartContent()) 
    { 
     //Save file 
     MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files")); 

     Request.Content.ReadAsMultipart(provider); // don't know if this is really valid. 

     return provider.BodyPartFileNames.First().Value; 
    } 
    else 
    { 
     return "Invalid."; 
    } 
} 

免責聲明:我並沒有實際執行上面的代碼;我只是寫它來說明應該做什麼。

+2

您不應該調用Wait(),因爲它會導致線程被阻塞。相反,這個'UploadFile()'函數本身應該是異步的。 – marcind

+0

我嘗試了你的第一個建議,但是在啓動方法中出現了錯誤:啓動可能不會在具有空操作的任務上調用。關於非異步 - 我不確定。我不認爲有這樣一種稱爲ReadAsMultipart(AFAIK)的方法。 – Rivka

+0

@marcind你是對的,這是一個尷尬的構造。如果可能的話,這就是我提出同步替代方案的原因。這是因爲我們有兩個順序執行的任務,當他們完成時我們需要控制 - 我認爲這裏沒有任何異步。 – GolfWolf