2011-08-18 492 views
4

我有一個相當複雜的程序,所以我不會在這裏存儲所有東西。下面是一個簡化版本:C# - 後臺工作人員?

class Report { 
    private BackgroundWorker worker; 

    public Report(BackgroundWorker bgWorker, /* other variables, etc */) { 
     // other initializations, etc 
     worker = bgWorker; 
    } 

    private void SomeCalculations() { 
     // In this function, I'm doing things which may cause fatal errors. 
     // Example: I'm connecting to a database. If the connection fails, 
     // I need to quit and have my background worker report the error 
    } 
} 


// In the GUI WinForm app: 
// using statements, etc. 
using Report; 

namespace ReportingService { 
    public partial class ReportingService : Form { 

     // My background worker 
     BackgroundWorker theWorker = new BackgroundWorker() { 
      WorkerReportsProgress = true 
     }; 

     // The progress changed event 
     void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { 
      // e.UserState and e.ProgressPercentage on some labels, etc. 
     } 

     // The do work event for the worker, runs the number crunching algorithms in SomeCalculations(); 
     void worker_DoWork(object sender, DoWorkEventArgs e) { 
      Report aReport = e.Argument as Report; 

      aReport.SomeCalculations(); 
     } 

     // The completed event, where all my trouble is. I don't know how to retrieve the error, 
     // or where it originates from. 
     void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
      // How, exactly, do I get this error message? Who provides it? How? 
      if (e.Error != null) { 
       MessageBox.Show("Error: " + (e.Error as Exception).ToString()); 
      } 
      else if (e.Cancelled) { 
       MessageBox.Show("Canceled"); 
      } 
      // operation succeeded 
      else { 
       MessageBox.Show("Success"); 
      } 
     } 

     // Initialization of the forml, etc 
     public ReportingService() { 
      InitializeComponent(); 

      theWorker.ProgressChanged += worker_ProgressChanged; 
      theWorker.DoWork += worker_DoWork; 
      theWorker.RunWorkerCompleted += worker_RunWorkerCompleted; 
     } 

     // A button that the user clicks to execute the number crunching algorithm 
     private void sumButton_Click(object sender, EventArgs e) { 
      Report myReport = new Report(theWorker, /* some other variables, etc */) 
      theWorker.RunWorkerAsync(myReport); 
     } 
    } 
} 

這裏是我的邏輯,並請糾正我,如果我要對這個錯誤的方法:

  1. 我抽象類出來的GUI的,因爲它是〜2000行和需要成爲它自己的自我包含的對象。

  2. 我將後臺工作者傳入我的課程,以便我可以報告我的數字處理進度。

我不知道該怎麼做是讓後臺工作人員知道我的課內發生了錯誤。爲了使RunWorkerCompleted參數成爲一個異常,我的try/catch塊需要去哪裏,我應該在catch塊中做什麼?

感謝您的幫助!

編輯:

我試過下面的東西來測試錯誤處理:

記住我破壞我的數據庫連接字符串故意收到一條錯誤消息。

在我的班級我做的:

// My number crunching algorithm contained within my class calls a function which does this: 

// try { 
    using (SqlConnection c = GetConnection()) { // note: I've corrupted the connection string on purpose 
     c.Open(); // I get the exception thrown here 
     using (SqlCommand queryCommand = new SqlCommand(query, c)) { /* Loop over query, etc. */ } 
     c.Close(); 
    } 
// } catch (Exception e) { } 

1. 從我的理解,未處理的異常被強制轉換爲RunWorkerCompletedEventArgsError部分?當我嘗試這一點,我得到如下:

// In my winform application I initialize my background worker with these events: 

void gapBW_DoWork(object sender, DoWorkEventArgs e) { 
    Report aReport = e.Argument as Report; 
    Report.Initialize(); // takes ~1 minute, throws SQL exception 
    Report.GenerateData(); // takes around ~2 minutes, throws file IO exceptions 
} 

void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
    if (e.Error != null) { // I can't get this to trigger, How does this error get set? 
     MessageBox.Show("Error: " + (e.Error as Exception).ToString()); 
    } 
    else if (e.Cancelled) { 
     MessageBox.Show("Canceled: " + (e.Result).ToString()); 
    } 
    else { 
     MessageBox.Show("Success"); 
    } 
} 

Visual Studio的說,我的應用程序扼流圈c.Open()與未處理的異常失敗。

2. 當我把一個try/catch塊在我的DoWork功能:

void gapBW_DoWork(object sender, DoWorkEventArgs e) { 
    try { 
     Report aReport = e.Argument as Report; 
     aReport.Initialize(); // throws SQL exceptions 
     aReport.GenerateData(); // throws IO file exceptions 
    } 
    catch (Exception except) { 
     e.Cancel = true;  
     e.Result = except.Message.ToString(); 
    } 
} 

void gapBW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
    if (e.Error != null) { // I can't get this to trigger, How does this error get set? 
     MessageBox.Show("Error: " + (e.Error as Exception).ToString()); 
    } 
    else if (e.Cancelled) { 
     MessageBox.Show("Canceled: " + (e.Result).ToString()); 
    } 
    else { 
     MessageBox.Show("Success"); 
    } 
} 

我得到了TargetInvocationException了未處理的Program.cs中的自動生成Application.Run(new ReportingService());線。我在RunWorkerCompleted上放置了一個斷點,並可以看到e.Cancelled = true,e.Error = null和e.UserState = null。 e.Cancelled中包含的信息僅僅是「操作已被取消」。我想我從e.Result的無效轉換中收到TargetInvocationException(因爲它爲空)。我想知道的是,e.Error是如何來的,e.Canceled沒有包含任何有用的信息爲什麼的操作被取消了?

3. 當我試圖從內部的DoWork對異常俘獲設置e.Canceled = true;,我設法觸發我RunWorkerCompleted功能else if (e.Cancelled) {線。我以爲這是保留給請求作業被取消的用戶呢?我從根本上誤解了後臺工作人員的功能?

回答

2

我想這個小測試程序,它按預期工作:

static void Main(string[] args) 
{ 
    var worker = new BackgroundWorker(); 

    worker.DoWork += (sender, e) => { throw new ArgumentException(); }; 
    worker.RunWorkerCompleted += (sender, e) => Console.WriteLine(e.Error.Message); 
    worker.RunWorkerAsync(); 

    Console.ReadKey(); 
} 

但是當我運行在調試器中這PROGRAMM我也得到了消息,關於在罰球語句未處理的異常。但我只是再次按下F5,並繼續沒有任何問題。

+0

感謝大家的幫助,但這最終解決了我的問題。原來這個問題不是我對C#的誤解,而是我對Visual Studio的誤解。我養成了按F5運行我的程序的習慣,並認爲當一個異常未處理時,程序會崩潰。我沒有意識到你可以再次按下F5繼續處理。 –

+0

另外不要忘記按下F10,F11和Shift-F11的功能。 ;-)) – Oliver

+0

也許看看[這個問題](http://stackoverflow.com/questions/1044460/unhandled-exceptions-in-backgroundworker/1044610#1044610)也幫助你了。 – Oliver

1

如果發生任何錯誤,請在doEvent的catch塊中設置e.Cancel = true。設置WorkerSupportsCancellation屬性第一。

在DoWork事件中。

private void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     try 
     { 
      if(!backgroundWorkder.CancellationPending) 
      { 
       // .... 
      } 
     } 
     catch 
     { 
      if (bgWorker.WorkerSupportsCancellation && !bWorker.CancellationPending) 
      { 
       e.Cancel = true; 
       e.Result = "Give your error"; 
       return; 
      } 
     } 
    } 

OnRunWorkerCompleted方法。

private void BW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 

    if(e.Cancelled) 
     { 
      MessageBox.Show(e.Result.ToString()); 
      return; 
     } 
} 

如果您在DoEvent中沒有執行任何異常處理。 BackgroundWorker自己爲你做這個。

如果一個異常是一個異步操作過程中提出,類 將分配異常錯誤屬性。在訪問派生自 的類中的任何屬性之前,客戶端 應用程序的事件處理程序委託應檢查錯誤屬性 AsyncCompletedEventArgs;否則,該屬性將引發一個 TargetInvocationException,其InnerException屬性保留對Error的 引用。

如果操作被取消,Error屬性的值爲null。

在這種情況下。

private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    // First, handle the case where an exception was thrown. 
    if (e.Error != null) 
    { 
     MessageBox.Show(e.Error.Message); 
    } 
    else if (e.Cancelled) 
    { 
     // Next, handle the case where the user canceled the operation.   
    } 
} 

有關更多詳細信息,請參見here

2

你在正確的軌道上。在RunWorkerCompleted事件中,e.Error參數包含引發的任何異常。在這種情況下,你應該把你的

if (e.Error != null) {...} 

try您在運行您的後臺工作的catch塊,如果是有道理的。

+0

因此,我不需要圍繞我的Report類中的SQL查詢進行try/catch?當我刪除它時,我得到一個SqlException是由用戶代碼錯誤未處理。除非我的工作人員的工作完成,否則這個例外是不會發生的? –

+1

如果您正在調試,只需按繼續(默認情況下爲F5),並且控件應該流入您的RunWorkerCompleted事件處理程序,並且應該填充e.Error。 – Thebigcheeze

0

每次DoWork中的操作完成,取消或拋出異常時,都會觸發RunWorkerCompleted。然後在RunWorkerCompleted中檢查RunWorkerCompletedEventArgs,如果Error不爲null。當DoWork出現異常時,會自動設置錯誤屬性。在這種特殊情況下無需嘗試捕捉。

您正在正確的軌道上。