2013-02-21 28 views
5

我有一個基於3層的web應用程序。我想用我在業務邏輯層中使用try catch塊。在業務邏輯中使用try/catch塊是正確的還是我需要在我的UI層中使用它?在三層架構中放置try/catch的位置

請參閱我的DAL代碼。

Data Access Layer 

#region Insert in to Logbook 
public int Insert_LogBook(string Vehicle_Number, DateTime Vehicle_Booking_Date, TimeSpan Time_From, TimeSpan Time_To, int KM_Start, int KM_End, string Vehicle_Used_By, string Cost_Code, string Budget_Line, DateTime Entry_Date) 
{ 
    try 
    { 
     SqlCommand com = new SqlCommand("Insert_LogBook", con); 
     com.Parameters.Add("@Vehicle_Number", SqlDbType.NVarChar, 100).Value = Vehicle_Number; 
     com.Parameters.Add("@Vehicle_Booking_Date", SqlDbType.DateTime).Value = Vehicle_Booking_Date; 
     com.Parameters.Add("@Time_From", SqlDbType.Time).Value = Time_From; 
     com.Parameters.Add("@Time_To", SqlDbType.Time).Value = Time_To; 
     com.Parameters.Add("@KM_Start", SqlDbType.Int).Value = KM_Start; 
     com.Parameters.Add("@KM_End", SqlDbType.Int).Value = KM_End; 
     com.Parameters.Add("@Vehicle_Used_Byr", SqlDbType.VarChar, 100).Value = Vehicle_Used_By; 
     com.Parameters.Add("@Cost_Code", SqlDbType.NVarChar, 50).Value = Cost_Code; 
     com.Parameters.Add("@Budget_Line", SqlDbType.NVarChar, 50).Value = Budget_Line; 
     com.Parameters.Add("@Entry_Date", SqlDbType.DateTime).Value = Entry_Date; 
     con.Open(); 
     int res = com.ExecuteNonQuery(); 

    } 
    catch (Exception ex) 
    { 
     WebMsgBox.Show(ex.Message); 
    } 
    finally 
    { 
     con.Close(); 
     con.Dispose(); 

    } 
    return 1; 
} 
#endregion 

所以我應該使用它在我的bal或在UI層或我的代碼是好的。因爲如果我不在我的UI層中使用try/catch,它將不會捕獲異常(如果有的話)並顯示錯誤頁面。

回答

3

異常處理和投擲的東西,我看到誤解的時候,由我曾與大多數開發商。

  • 異常允許您在代碼中查找錯誤。
  • 他們停止正在運行的程序以防止業務受到傷害。
  • 它們允許您在合理預期的異常(移動應用上不可用的網絡)與意外的錯誤異常(如NullReferenceException)之間過濾。
  • 他們直截了當地指出發生錯誤的原因。
  • 它們允許每個組件添加一層關於上下文和狀態的信息以幫助調試,這是catch,wrap和throw模式。

可以和應該使用的try/catch /(終於)幾乎任何地方,但...

您可以使用捕捉只有當你知道哪些異常(S)很可能會發生,並且可以從恢復他們。您應該很少捕獲基本的Exception類型。允許所有其他錯誤冒泡並由程序員/測試人員/用戶找到。

如果要拋出另一個異常並將原始異常附加爲InnerException,則可能需要捕獲基本異常類型。

您不應該三思而後行,懶得創建自己的異常類型。例如,您可能需要編寫一個DataAccessException,並在InnerException中附加的層中捕獲異常時引發該異常。這樣,您的日誌記錄將記錄一個異常類型,它可以更準確地顯示發生錯誤的位置,並且調用代碼可以只選擇捕獲DataAccessException並執行重試或其他操作。

您也可以考慮使DataAccessException抽象和繼承更特定的異常,如SqlDataAccessException或SecurityDataAccessException。

如你所知,當你想確保某些代碼在發生錯誤時運行時,即使你不捕獲並處理異常本身,也可以最終使用它。在必須始終釋放資源的情況下,try/finally會成爲標準模式。

另外,如果可能的話,把try/catch語句的代碼最具體的補丁在你身邊能夠處理錯誤,允許在周圍代碼編碼錯誤崩潰的應用程序在哪裏。

您可能會想:「如果我還不知道哪些異常會被拋出,我該如何捕獲特定異常?」你應該在ExecuteNonSql方法中看到它們的記錄,這就是爲什麼用它拋出的異常來記錄你自己的API /組件是如此重要。使用XML註釋執行此操作,如果發送公共DLL,請打開XML註釋文件生成器。

這似乎是很多參加,但它不是在實踐中。當你在伐木和適當的異常處理/投擲方面投資時,你將能夠在幾分鐘內解決錯誤,你會感覺像冠軍,你很快就會學會與其他人的糟糕的代碼感到不安。:)

At在您的編程生涯的這個階段,我強烈建議閱讀Cwalina和Abrams的Framework Design Guidelines。它將幫助你快速做出所有這些類型的問題的正確選擇,你會發現使用自己的代碼和使用微軟的API(大部分)一樣快樂。

Luke

添加一些關於消息的信息。我在錯誤信息中使用了這種類型的東西。

「Can not {perform some function}。{exception exception} occurred。{提供補救建議或常見錯誤原因}請參閱{內部異常|更多日誌條目}。

例如,在一個應用程序自動保存狀態的組件:

... 
catch(FileNotFoundException fnfe) 
{ 
    string m = String.Format("Cannot save changes. A FileNotFoundException occurred. Check the path '{0}' is valid, that your network is up, and any removable media is available. Please see inner exception.", path); 

    _log.Error(m, fnfe); 

    throw new StorageLifecycleException(m, fnfe); 
} 
0

您應該將數據訪問和業務邏輯放入類庫中。 您不應該從BLL或DAL調用前端組件。 在BLL和DAL中創建日誌記錄使用類似log4net的東西將錯誤數據放在這裏。

然後向上拋出異常,如果你想通知用戶錯誤狀態。

2

你應該寫一個try-catch塊來處理異常。沒有這樣的東西,「總是在這裏或那裏試試看」。 我看你處理異常這樣的:

catch (Exception ex) 
{ 
    WebMsgBox.Show(ex.Message); 
} 

這是因爲以下幾個原因壞:

  1. 你趕上通用Exception型。前幾天我看到一個關於此的問題:https://stackoverflow.com/a/14727026/238682
  2. 您嘗試在數據訪問層中處理與WebMsgBox.Show的異常,該數據訪問層打破了圖層的邊界。

這個例子的另一個問題是一個小問題,但我認爲它在長期(整體代碼設計)中很重要。 您應該將錯誤處理邏輯與實際的應用程序邏輯分開。所以當你使用try-catch塊時,儘量減少裏面的邏輯,這樣你的代碼變得更加可讀。

public int Insert_LogBook(string Vehicle_Number, DateTime Vehicle_Booking_Date, TimeSpan Time_From, TimeSpan Time_To, int KM_Start, int KM_End, string Vehicle_Used_By, string Cost_Code, string Budget_Line, DateTime Entry_Date) 
{ 
    using(SqlCommand com = new SqlCommand("Insert_LogBook", con)) 
    { 
     com.Parameters.Add("@Vehicle_Number", SqlDbType.NVarChar, 100).Value = Vehicle_Number; 
     com.Parameters.Add("@Vehicle_Booking_Date", SqlDbType.DateTime).Value = Vehicle_Booking_Date; 
     com.Parameters.Add("@Time_From", SqlDbType.Time).Value = Time_From; 
     com.Parameters.Add("@Time_To", SqlDbType.Time).Value = Time_To; 
     com.Parameters.Add("@KM_Start", SqlDbType.Int).Value = KM_Start; 
     com.Parameters.Add("@KM_End", SqlDbType.Int).Value = KM_End; 
     com.Parameters.Add("@Vehicle_Used_Byr", SqlDbType.VarChar, 100).Value = Vehicle_Used_By; 
     com.Parameters.Add("@Cost_Code", SqlDbType.NVarChar, 50).Value = Cost_Code; 
     com.Parameters.Add("@Budget_Line", SqlDbType.NVarChar, 50).Value = Budget_Line; 
     com.Parameters.Add("@Entry_Date", SqlDbType.DateTime).Value = Entry_Date; 
     con.Open(); 
     int res = com.ExecuteNonQuery(); 

     return 1; 
    } 

} 

public void SomeMethodWhichUsesThatInsert() 
{ 
    try 
    { 
     //call Insert_LogBook 
    } 
    catch(SomeException e) 
    { 
     //handle 
    } 

} 
4

這只是依賴,你可以在兩層都使用try catch塊。

但問題不在於你在使用異常處理代碼的地方;問題在於你如何使用。在您提供的示例中,您正在捕獲泛型exception,但您不知道它是否爲SqlException或任何其他異常。

一般來說,

  1. 只捕獲你可以處理(在本例中抓SqlException,並非所有的例外)

  2. 顯示一個用戶友好的消息(例外在你的例子只是顯示錯誤信息,使用戶沒有意義)

  3. 日誌異常

  4. 處理髮生的異常;如果它是DAL相關的異常,則在DAL層處理它,如果它是與UI相關的異常,則在UI層處理它。

+0

那麼能否請您詳細說明使用我的代碼?我沒有使用3層 – Gaurav 2013-02-21 09:32:21

+0

+1在錯誤信息上。我們都是用書呆子的信息長大的,所以我們認爲信息必須聽起來很討厭。不,他們應該聽起來像一個朋友,用簡單的詞語來解釋問題,甚至提供可能的解決方案 – 2013-02-21 11:09:04