2014-01-07 46 views
1

例外,我想生成打開一個SQL連接的方法:處理來自另一個功能

public SqlConnection openConnection (string connString) 
{ 
    using (SqlConnection conn = new SqlConnection(connString)) 
    { 
     try 
     { 
      conn.Open(); 
     } 
     catch (Exception) 
     { 
      OrdersFaultException connEx = new OrdersDBInternalFaultException(); 
      throw new FaultException<OrdersFaultException>(connEx); 
      return null; 
     } 
     return conn; 
    } 
} 

我想知道如果上面的代碼是一個正確的方法是什麼?或者我應該在openConnection中引發異常,並在調用openConnection的函數中處理異常?

+5

您不應該在這裏使用「使用」:您正在返回無用的連接 –

+1

自從您使用'using'語句後,連接一旦返回就會關閉。但是,我根本不會使用這種方法。使用'using'語句需要的連接。請注意,打開連接並不意味着它將通過[connection-pooling](http://msdn.microsoft.com/zh-cn/library/8xx3tyca(v = vs.110).aspx)進行物理打開,它是隻是標記爲「正在使用」。 –

+1

「返回null」無法訪問 – Jordi

回答

2

你不能使用使用在這裏:沒有人想要一個配置連接。 所以,你可以模擬使用

public SqlConnection openConnection (string connString) { 
    SqlConnection conn = null; 

    try { 
    conn = new SqlConnection(connString)); 

    return conn; 
    } 
    catch (Exception e) { // <- Probably, you should catch more specific exception, e.g. DataException 
    // This "if" (that's a part of typical using emulation scheme) is redundant 
    // you may safely remove it unless there's no addition code in the "try" 
    // see Todd Bowles comments 
    if (!Object.ReferenceEquals(null, conn)) 
     conn.Dispose(); 

    // Do not forget to pass the reason (intitial exception e) 
    // when throwing your own one 
    throw new OrdersDBInternalFaultException(e);  
    } 
} 
+0

我的理解是,如果在構造函數中拋出異常,對象永遠不會完成實例化,這意味着conn在catch塊內將始終爲null。您的處置電話將永遠不會執行。 –

+0

@Todd Bowles:是的,你是對的,例外是阻止conn被分配;但由於「conn」是一個局部變量,它沒有被null(它包含垃圾)初始化 - 這就是爲什麼必須顯式地初始化和檢查局部變量 –

+1

我的意思是說,處置是毫無意義的。在出現錯誤的情況下,conn將始終爲空。在沒有錯誤的情況下,catch塊將不會被執行。 –

1

德米特里和蒂姆都是正確的。

對於異常處理雖然在你的情況下,當你試圖打開一個連接,因此如果沒有達到所需的功能,則應拋出異常。因此,該用戶知道該功能必須執行的功能尚未執行。

2

您應該處理與相同的抽象程度爲的異常,作爲引發這些異常的代碼。

這意味着,負責打開數據庫的代碼可能會拋出數據庫相關異常,負責處理訂單的代碼可能會拋出與訂單相關的異常。

你可能想扔從的openConnection方法有點像DatabaseConnectionException(有相應的錯誤消息),然後從什麼地方提出你OrdersDBInternalFaultException,你的情況下,DatabaseConnectionException把你訂單的護理髮生了

注意:拋出異常後,您不需要返回null。這種回報永遠無法達成,並且是一個死代碼。

1

早在C++的日子裏,除了安全是真正重要的事情。如果我沒有記錯的話,Sutter的Exceptional C++對此進行了相當詳細的描述。

現在有一件事可能會讓人覺得奇怪,那就是C++中沒有'finally'。如果您在堆棧中添加創建對象,則在您退出示波器時它將自動「處置」。說穿了,範圍就是'{'和'}'會爲你管理的事情。

如果你在做C#,異常處理仍然很重要,但在不同的層面。在所有情況下,你必須確保你的整體狀態保持一致。那麼這是如何工作的?

using無非是一個圍繞try,catchfinally的包裝。它基本上在finally中創建了Dispose,從而確保當您退出範圍時(無論出於何種原因),該對象被清理(即假設它實現了IDisposable)。正因爲如此,以確保對象最簡單的方法是正確的清理,是通過:

  1. 創建一個實例,
  2. 使用實例,
  3. 處置實例

(在此爲了)

如:

using (var connection = new Connection()) // create instance 
{ 
    // use connection instance here 

} // dispose connection instance -> implicitly created by 'using'. 

重複使用代碼

如果你不想一遍寫的代碼,這是一件好事(f.ex.使用連接字符串),您可以手動處理對象,也可以使用簡單的回調函數來保持流程相同。

private void Execute(Action<Connection> action) 
{ 
    using (var connection = new Connection()) // create instance 
    { 
     // use connection instance here 

     action(connection); // callback; this won't break the described mechanism 

    } // dispose connection instance -> implicitly created by 'using'. 
} 

爲什麼不使用using正確不好

因此,這裏的返回,而不考慮任何使用該連接的實現:

// Buggy connection builder 
private Connection CreateConnection() 
{ 
    Connection tmp = new Connection(); 
    // do some stuff with tmp, might throw 

    return tmp; 
} 

private void Client() 
{ 
    using (var connection = CreateConnection()) 
    { 
     // do something with the connection, assuming we're safe 
    } 
} 

這似乎都不錯,還行,但它其實是不正確的。你知道嗎?

在它所說的「用tmp做某些事情」的部分,可能會出錯,導致該方法拋出。發生這種情況時,該對象將永遠不會返回給客戶端,並且保留活動Connection而無需參考。即使你認爲你很安全,但實際上並不是。

關於構造和異常

如果你注意,這也意味着事情都可能出錯的構造函數。如果你創建了一個物體,如果狗屎撞上了風扇,那麼這個物體沒有被正確清理,你也有一個懸掛的物體。例如:

// Buggy connection wrapper 
public class ConnectionWrapper : IDisposable 
{ 
    public ConnectionWrapper() 
    { 
     this.connection = new Connection(); 
     // do some other stuff, some of which might throw 
    } 

    private Connection connection; 
    // ... 

    // IDisposable stuff. 
} 

private void Client() 
{ 
    using (var connection = new ConnectionWrapper()) 
    { 
     // do something with the connection, assuming we're safe 
    } 
} 

此外,構造函數中的代碼可能會引發異常。在這種情況下,構造函數不會將對象返回給客戶端,這意味着IDisposable將不會在usingfinally中調用。這並不意味着IDisposable在這裏是錯誤的 - 事實上它不是!

這裏出現的問題是我們創建了一個不返回並且未清理的IDisposable對象。我們可以通過添加一個catch來解決這個問題,它在清理連接後簡單地重新拋出異常。