2013-09-27 144 views
1

我使用ContinueWith運行一項任務,該任務執行雙重任務:如果任務成功完成,則處理結果,如果發生錯誤則處理任何異常。但是,下面的代碼將不能正確地處理任何異常,並將其註冊爲unhandeled並帶來降低程序(張貼所以有些縮短,可能不是完美的):TPL任務繼續執行結果和異常導致未處理的異常

void _SqlServerDatabaseListLoader() 
{ 
    _ClearSqlHolders(true, false); 
    _SqlConnectionStringHolder.Database = "master"; 
    if (_SqlConnectionStringHolder.IsComplete) 
    { 
     //Could time out put on its own thread with a continuation back on the UI thread for the popup 
     _TaskCanceller = new CancellationTokenSource(); 
     _TaskLoader = Task.Factory.StartNew(() => 
     { 
      IsLoadingSqlServerDatabaseList = true; 

      using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder)) 
      { 
       // Open connection 
       con.Open(); //If this cause an error (say bad password) the whole thing bombs 

       //create a linq connection and get the list of database names 
       DataContext dc = new DataContext(con); 
       return new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection()); 
      } 
     }).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Result, antecendant.Exception), 
      _TaskCanceller.Token, 
      TaskContinuationOptions.None, 
      TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
} 

void _SqlServerDatabaseListLoaderComplete(ObservableCollection<string> DatabaseList, AggregateException ae) 
{ 
    //Just show the first error 
    if (ae != null) 
     ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error"); 

    if(DatabaseList != null) 
     SqlServerDatabaseList = DatabaseList 

    //Set the running indicator 
    _TaskLoader = null; 
    _TaskCanceller = null; 
    IsLoadingSqlServerDatabaseList = false; 
} 

我使用TaskContinuationOptions.None拉這一關,我認爲這是正確的。這在基類中聲明這上面的類繼承自:

protected Task _TaskLoader; 
protected CancellationTokenSource _TaskCanceller; 

如果我是不會導致錯誤的情形下運行,一切順利,我得到我的數據庫清單。但是,如果出現錯誤,說某人爲此SQL Server登錄憑據提供了錯誤的密碼,則不會處理該錯誤。

但如果我刪除傳遞Result參數的選項,一切正常,因爲它應該和異常引起:

void _SqlServerDatabaseListLoader() 
{ 
    _ClearSqlHolders(true, false); 
    _SqlConnectionStringHolder.Database = "master"; 
    if (_SqlConnectionStringHolder.IsComplete) 
    { 
     //Could time out put on its own thread with a continuation back on the UI thread for the popup 
     _TaskCanceller = new CancellationTokenSource(); 
     _TaskLoader = Task.Factory.StartNew(() => 
     { 
      IsLoadingSqlServerDatabaseList = true; 

      using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder)) 
      { 
       // Open connection 
       con.Open(); 

       //create a linq connection and get the list of database names 
       DataContext dc = new DataContext(con); 

       //HAVE TO SET IN THE THEAD AND NOT RETURN A RESULT 
       SqlServerDatabaseList = new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection()); 
      } 
     }).ContinueWith(antecendant => _SqlServerDatabaseListLoaderComplete(antecendant.Exception), 
      _TaskCanceller.Token, 
      TaskContinuationOptions.None, 
      TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
} 

void _SqlServerDatabaseListLoaderComplete(AggregateException ae) 
{ 
    //Just show the first error 
    if (ae != null) 
     ToolkitDialog.ShowException(ae.InnerExceptions[0], ToolkitDialogType.Error, CustomDialogButtons.OK, "Error:", "Database List Error"); 

    //Set the running indicator 
    _TaskLoader = null; 
    _TaskCanceller = null; 
    IsLoadingSqlServerDatabaseList = false; 
} 

我認爲我不完全理解TPL是怎麼想的工作。我試圖創造更多的一個ContinueWith,這似乎沒有什麼區別。謝謝你的幫助。

回答

2

的問題是,取Task<T>.Result將提高AggregateException在這一點,發生,才能真正抓住異常,並防止你的方法被調用。

一種選擇是使用兩個延續 - 一個發生異常時進行,一個是當它不:

_TaskLoader = Task.Factory.StartNew(() => 
    { 
     IsLoadingSqlServerDatabaseList = true; 

     using (SqlConnection con = new SqlConnection(_SqlConnectionStringHolder)) 
     { 
      // Open connection 
      con.Open(); 

      //create a linq connection and get the list of database names 
      DataContext dc = new DataContext(con); 

      //HAVE TO SET IN THE THEAD AND NOT RETURN A RESULT 
      SqlServerDatabaseList = new ObservableCollection<string>(dc.ExecuteQuery<string>("select [name] from sys.databases").ToObservableCollection()); 
     } 
    }); 

    // This method is called if you get an exception, and processes it 
    _TaskLoader.ContinueWith(antecendant => _SqlServerDatabaseListLoaderFaulted(antecendant.Exception), 
     _TaskCanceller.Token, 
     TaskContinuationOptions.OnlyOnFaulted, 
     TaskScheduler.FromCurrentSynchronizationContext()); 

    // This method is called if you don't get an exception, and can safely use the result 
    _TaskLoader.ContinueWith(antecendant => _SqlServerDatabaseListLoaderCompleted(antecendant.Result), 
     _TaskCanceller.Token, 
     TaskContinuationOptions.NotOnFaulted, 
     TaskScheduler.FromCurrentSynchronizationContext()); 

另一種選擇將是通過Task<T>本身(antecendant)作爲該方法的論點。然後您可以檢查task.Exception,如果它不爲空,則顯示該異常,否則處理結果。

+0

謝謝里德。很有意思。我嘗試了,實際上並沒有得到它去。感謝關於通過整個任務但同樣的事情的想法。它不喜歡「.Result」 - 它給出了它不存在的設計時錯誤(兩種情況)。真奇怪。如果我使用_TaskLoader,它會給我簽名錯誤,說它不能將任務投射到任務>。如果我設置了「受保護的任務> _TaskLoader」,我仍然收到錯誤消息。但是如果我做了「var x = Task > .Factory.StartNew(()=> ....」它可以。 – Ernie

+1

@Ernie之前,你正在爲你的* continuation設置_TaskLoader *而不是原始任務。這可能就是爲什麼你有類型不匹配 –

+0

AHHHH ......就是它裏德!我認爲這一直是我的問題,包括當我試圖在發佈之前用兩個ContinueWith發佈時分解它。 。感謝所有的幫助! – Ernie