2017-05-31 56 views
0

我有一個ASP.NET Web API,它使用SqlConnection連接到數據庫。我有一個數據訪問層類,它有一個包含連接的實例變量。我這樣做了幾個方面的原因:在ASP.NET中正確配置SqlConnection?

  1. 調用代碼可以覆蓋在DAL類(例如,用於測試代碼)
  2. 也有一些情況下API控制器需要打開的構造函數的連接字符串一個SQL連接,開始一個事務,然後在提交(或回滾)事務之前調用DAL類中的幾個方法。因此,關閉並重新打開每個方法的連接將不起作用,因爲我必須保持連接處於打開狀態(甚至保留SqlTransaction對象的範圍 - 我會通過將它作爲一個實例變量來實現),以便不必該事務在DAL調用之間回滾。
  3. 它還簡化了代碼的可讀性,所以你不會複製遍佈各處的代碼來打開SQL連接。

當我對API進行壓力測試時,通過每秒提供數百次請求,我碰到了SQL連接池耗盡問題。進一步調查顯示,這似乎是因爲SQL連接沒有被處置。

我明白IDisposable模式,但我不確定在這種情況下如何使用它。這裏是我的問題:

  1. 使用using塊,或try/catch/finally塊,都需要被創建,使用和單一方法中完成的對象。在可能需要跨多個方法調用持續存在的事務上面的示例中,這是不可能的。
  2. 微軟(和SO其他職位)建議針對乾脆把呼叫Dispose()在對象的析構,而不是建議你做任何的我在問題中指定的選項1.
  3. 微軟還表示,你不應該實施IDisposable你自己只是爲了包裝另一個管理對象的方法Dispose,但是你應該「在完成對象時簡單地調用對象的Dispose()。」在控制器完成使用SQL連接後,如果我不確定從DAL內部執行此操作,我該怎麼做? (另外,這意味着我必須重構控制器來包裝每次調用DAL在using塊,所以它只是拖延時間的道路。)

對我來說,理想解決方案將是當控制器完成處理並且將其對服務器的響應返回以傳送到前端時,能夠以某種方式安排在SqlConnection對象上調用Dispose方法。要做到這一點「手工」,我將不得不違反上面的第3點,並在我的DAL上創建我自己的Dispose方法,然後簡單地調用SqlConnection的Dispose。此外,這意味着我必須重構所有控制器中的許多方法以包裝using塊中的所有DAL訪問權限。看起來ASP.NET在控制器返回時不會自動調用Dispose,這就是連接泄漏的原因。

在任何一種情況下,它也會產生更詳細的代碼。例如:

// we only need one method call from the DAL, so let's be compact 
string someData = new DAL().GetSomeData(someParam); 

現在已經寫出爲:

// we have to initialize here to keep the variable from falling out of scope after the using blocks 
// we also must provide some value because the using block implies try/catch. 
string someData = ""; 
using (DAL d = new DAL()) { 
    someData = d.getSomeData(someParam); 
} 

什麼是實現這個建議的方法是什麼?

在一個更通用的平面上,您如何處理一個必須在方法調用之間持續存在的可丟棄對象(例如作爲實例變量)?在像try/catch/finallyusing這樣的結構中使用一次性物品的需要似乎限制了它們的使用,僅限於可以在單一方法中創建和處理物體的情況。

+1

如果您使用DI容器 - 您可以讓它在請求結束時處理您的資源。雖然我個人更喜歡自己管理關係 - 當我需要時打開,做些東西,關閉。 – Evk

回答

0

由於MSDN recommends你應該使用

using(var cn = new SqlConnection(xx)){ cn.Open(); }

注意:不是每次

關閉SqlConnection吸引了來自連接池打開的連接無法打開新的連接,如果 一個是可用。否則,它建立到SQL Server實例的新連接。

而且爲了控制好你的交易 - 你可以只使用TransactionScope()(雖然它可能需要MSDTC in some case`s


否則,如果使用TransactionScope是不是一種選擇,並且要明確控制您的交易,唯一的選擇是一起傳遞連接。因爲它是discussed in this POST

你可以通過兩種方式做「傳遞」。手動或使用依賴注入(DI)。所有DI容器都允許您控制實例的生命週期。所以你的情況可能是一個「每個請求一生」 樣本可以this post

+0

這是大多數情況,但您需要確保連接字符串不會禁用連接池,但這不是默認行爲,但可能會發生。 – Zalomon

+0

基於這些答案,似乎沒有辦法做到這一點,至少沒有TransactionScope ...這太糟糕了,它似乎打敗了將DAL分離成單獨的類的整個觀點。只要Web API必須管理創建和處置連接,您就失去了將DAL分開的好處(例如,只需編寫新的DAL,甚至使用模擬的DAL進行測試即可替換DBMS)。看起來最乾淨的選擇可能是簡單地將DAL設置爲一次性使用,然後在Web API中爲DAL使用',即使這違反了「推薦」的做法。 – fdmillion

+0

您引用的文章描述了我的確切問題:「有時候有助於提升與您所從事工作的班級成員的聯繫,但這可能會造成尷尬局面 - 並且控制連接的生命週期和處置變得複雜對象(因爲它通常排除使用using語句)「。所以唯一能做到這一點的方法是使DAL一次性使用,並在每個控制器方法中使用''using' ... – fdmillion

0

可以找到關於這一段。

在一個更通用的平面上,您如何實際處理必須在方法調用之間持續存在的一次性對象(例如作爲實例變量)?像try/catch/finally或using這樣的構造中使用一次性構件的需求似乎限制了它們的使用,只能用於可以在單一方法中創建和處理對象的情況。

作爲一個經驗法則負責實例一次性對象應是一個負責設置它的方法,它是確定沿着傳遞作爲參數傳遞給其它方法從實例化的方法,但調用,這些方法不應該負責處理這些對象。這個規則的例外可能是如果你實例化對象以注入到另一個類中,但是在這種情況下,這個類應該實現IDisposable,並且應該在調用者使用它之後處理。

+0

這很有道理,它只是不遵循我正在使用的邏輯模型...正如我上面所說的,只要您需要Web API創建'SqlConnection'對象並將它們傳遞到DAL中,您就失去了分離DAL的任何好處(例如只需更換DAL即可完全替換DBMS )。這同樣適用於其他場景。 DI當然有它的位置,但它有時也違反了「組件」的想法,在這種情況下,你有一個類可以提供你需要訪問數據庫的所有東西... – fdmillion

+0

我認爲你違反了你的設計時該API有時會實例化SQLConnection而不是DAL;事實上,API不應該知道任何關於SQLConnection的存在,您應該有一個共同的入口點來創建和處理連接 – Zalomon