1. 程式人生 > >dispose()與close()的區別 及pooling連線池相關

dispose()與close()的區別 及pooling連線池相關

Dispose了,就必須再Create一次
而Close()後,還可以再Open(),
而Dispose後,物件都不存在了,就不能Open()了
Dispose是對於物件自身而言的,Close是對於連線資料庫而言的
其它都是誤導...

以下是相關知識點:

1.SqlConnection conn = new SqlConnection(strConnection)時,如果原來的連線已經關閉,此時需要開啟一個新連線時,只要strConnection與上次的一樣,就會又從連線池中取回原來的物件。Close時,SqlConnection的例項並不會被釋放,它僅僅是被關閉了,並放置到了另一個地方,當下次需要時,會再次取出來。這樣可以避免建立例項的開例。哈,不過,我認為這樣的意義不是很大,只是.NET框架提供的一種管理優化機制而已(因為最消耗時間的還是Open與Close)

連線池允許應用程式從連線池中獲得一個連線並使用這個連線,而不需要為每一個連線請求重新建立一個連線。一旦一個新的連線被建立並且放置在連線池中,應用程式就可以重複使用這個連線而不必實施整個資料庫連線建立過程。

當應用程式請求一個連線時,連線池為該應用程式分配一個連線而不是重新建立一個連線;當應用程式使用完連線後,該連線被歸還給連線池而不是直接釋放。

資料庫連線池中可能存在著多個沒有被使用的連線一直連線著資料庫(這意味著資源的浪費)。

使用連線池的最主要的優點是效能。建立一個新的資料庫連線所耗費的時間主要取決於網路的速度以及應用程式和資料庫伺服器的(網路)距離,而且這個過程通常是一個很耗時的過程。而採用資料庫連線池後,資料庫連線請求可以直接通過連線池滿足而不需要為該請求重新連線、認證到資料庫伺服器,這樣就節省了時間。

包含在ADO.NET中的每個.NET資料提供程式都可實現連線池。

每一個連線池都與一個獨立的連線字串及其事務上下文關聯。每次開啟一個新的連線,資料提供者會嘗試將指定的連線字串與連線池的字串進行匹配。如果匹配失敗,資料提供者建立一個新的連線並將它加入連線池。連線池被建立之後,除非程序結束,否則不會被拆除。有人認為這種處理方式會影響效能,其實不然,維護一個不活動的或者空的連線池不需要多少開銷。

連線池建立之後,系統會建立一些連線物件並將它們加入連線池,直至達到額定的最小連線物件數量。以後,系統會根據需要新建和加入連線物件,一直到達最大連線物件數量限額為止。如果程式請求一個連線物件時沒有空閒的連線物件可用,且連線池裡面的物件數量已達到上限,則請求被放入佇列,一旦有連線被釋放回緩衝池就立即取出使用。

注意:如果是併發訪問裡,一個連線正在被使用,而此時又啟動了另一個程序或執行緒,則也會建立一個新的連線並放入連線池之中。只有第一次呼叫關閉後,第二次再次呼叫的時候,如果是相同的連線字串,才會直接呼叫連線池中的連線。

關閉一個連線時,連線物件被返回給連線池以便重用,但這時實際的資料庫連線並未被拆除。如果禁用了連線池,則實際的資料庫連線也被關閉。這裡必須強調的一點時,連線物件使用完畢後應當顯式關閉並將它返回給連線池,不要依靠垃圾收集器來釋放連線。

呼叫Close或Dispose方法可以將連線釋放回連線池。只有當生存期結束或出現嚴重錯誤時,連線物件才會被從連線池刪除。

對於.NET應用程式而言,預設為允許連線池。(這意味著你可以不必為這件事情做任何的事情)當然,如果你可以在SQLConnection物件的連線字串中加進Pooling=true;確保你的應用程式允許連線池的使用。 ADO.NET預設為允許資料庫連線池,如果你希望禁止連線池,可以使用如下的方式:
     1) 使用SQLConnection物件時,往連線字串加入如下內容:Pooling=False;
     2) 使用OLEDBConnection物件時,往連線字串加入如下內容:OLE DB Services=-4;

注意:在達到最小連線物件數量之前,是會不斷建立新的連線物件的,另外,最大連線數量,實際上是指出了連線的併發數。

======================

static不保證你在多執行緒模式下仍是執行緒安全的,但總的來說,多一個連線的消耗在這裡並不會造成什麼影響,但有可能造成執行時的異常。將sqlConnection設定成static,一般情況下,不會有什麼影響。
實際上在連線池之中,已經實際上類似單例的維護模式,所以,你即使是不斷地new SqlConnection,也不會造成什麼問題的。需要注意的是,維護一個長時間開啟的SqlConnnection在WinForm裡一般不會有什麼問題,

===================================

ADO.Net連線池和連線字串詳細說明
Connection Pool 是什麼呢 ?
每當程式需要讀寫資料庫地時候。Connection.Open()會運用ConnectionString連線到資料庫,資料庫會為程式建立一個連線,並且維護開啟狀態,此後程式就可以運用T-SQL語句來查詢/更新資料庫。當執行到Connection.Close()後,資料庫就會關閉當前地連線。很好,一切看上去均為如此有條不紊。

當然如果我地程式需要不定時地開啟和關閉連線,(比如說 ASP.NET 或是 Web Service ),例如當Http Request傳送到伺服器地時候、,我們需要開啟Connection 然後運用Select* from Table 返回一個DataTable/DataSet給客戶端/瀏覽器,然後關閉當前地Connection。那每次都Open/Close Connection 如此地頻繁操作對於整個系統擇定確定就成了一種浪費。

ADO.Net Team就給出了一個比較好地解決方法。將先前地Connection儲存起來,當下一次需要開啟連線地時候就將先前地Connection 交給下一個連線。這就是Connection Pool。

-          Connection Pool 如何工作地?
首先當一個程式執行Connection.open()時候,ADO.net就需要判斷,此連線是否支援Connection Pool (Pooling 預設為True),如果指定為False, ADO.net就與資料庫之間建立一個連線(為了避免混淆,所有資料庫中地連線,都運用”連線”描述),然後返回給程式。如果指定為True,ADO.net就會根據ConnectString建立一個Connection Pool,然後向Connection Pool中填充Connection(所有.net程式中地連線,都運用”Connection”描述)。填充多少個Connection由Min Pool Size (預設為0)屬性來決定。例如如果指定為5,則ADO.net會一次與SQL資料庫之間開啟5個連線,然後將4個Connection,儲存在Connection Pool中,1個Connection返回給程式。

當程式執行到Connection.close() 地時候。如果Pooling 為True,ADO.net 就把當前地Connection放到Connection Pool並且維護與資料庫之間地連線。相應情況下還會判斷Connection Lifetime(預設為0)屬性,0代表無限大,如果Connection存在地時間超過了Connection LifeTime,ADO.net就會關閉地Connection相應情況下斷開與資料庫地連線,而不是重新儲存到Connection Pool中。(這個設定重點用於群集地SQL 資料庫中,達到負載平衡地目地)。如果Pooling指定為False,則直接斷開與資料庫之間地連線。

然後當下一次Connection.Open() 執行地時候,ADO.Net就會判斷新地ConnectionString與原先儲存在Connection Pool中地Connection地connectionString是否一致。(ADO.Net會將ConnectionString轉成二進位制流,所以也就是說,新地ConnectionString與儲存在Connection Pool中地Connection地ConnectionString必須完全一致,即使多加了一個空格,或是修改了Connection String中某些屬性地次序都會讓ADO.Net認為這是一個新地連線,而從新建立一個新地連線。所以如果您運用地UserID,Password地認證方式,修改了Password也會導致一個Connection,如果運用地是SQL地整合認證,就需要儲存兩個連線運用地是同一個)。然後ADO.net需要判斷當前地Connection Pool中是否有可以運用地Connection(沒有被其他程式所佔用),如果沒有地話,ADO.net就需要判斷ConnectionString設定地Max Pool Size (預設為100),如果Connection Pool中地所有Connection沒有達到Max Pool Size,ADO.net則會再次連線資料庫,建立一個連線,然後將Connection返回給程式。如果已經達到了MaxPoolSize,ADO.net就不會再次建立任何新地連線,而是等待Connection Pool中被其他程式所佔用地Connection釋放,這個等待時間受SqlConnection.ConnectionTimeout(預設是15秒)限制,也就是說如果時間超過了15秒,SqlConnection就會丟擲超時錯誤(所以有時候如果SqlConnection.open()方法丟擲超時錯誤,一個可能地原因就是沒有及時將原先地Connnection關閉,相應情況下Connection Pool數量達到了MaxPoolSize。)如果有可用地Connection,從Connection Pool 取出地Connection也不是直接就返回給程式,ADO.net還需要檢查ConnectionString地ConnectionReset屬性(預設為True)是否需要對Connection 最一次reset。這是由於,原先從程式中返回地Connection可能已經被修改過,比如說運用SqlConnection.ChangeDatabase method 修改當前地連線,此時返回地Connection可能就已經不是連線當前地Connection String指定地Initial Catalog資料庫了。所以需要reset一次當前地連線。當然由於所有地額外檢查都會增大ADO.net Connection Pool 對系統地開銷。


-          Connection Pool 如何設定呢?
要修改Connection Pool 唯一地方式就是通過設定Connection String來完成。

Pooling (true)
When true, the connection is drawn from the appropriate pool, or if necessary, created and added to the appropriate pool.
此屬性代表是否需要運用到連線池,預設為True,如果指定為False,不運用連線池。

Connection Lifetime (0)
When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.
A value of zero (0) will cause pooled connections to have the maximum time-out.
這個屬性表示一個Connection地有效時間,如果一個Connection返回到ConnectionPool地時候,超過了Connection LifeTime時間,這個連線不會再次放到Connection。當下一個請求發來時,ADO.Net會新建一個Connection。
這個屬性重點運用於群集地SQL資料庫中,用於負載平衡。

Enlist (True)
When true, the pooler automatically enlists the connection in the current transaction context of the creation thread if a transaction context exists.

Max Pool Size (100)
The maximum number of connections allowed in the pool.

Min Pool Size (0)
The minimum number of connections maintained in the pool.

ConnectionReset (True)
Gets or sets a Boolean value that indicates whether the connection is reset when drawn from the connection pool.
The value of the ConnectionReset property or true if no value has been supplied.
This property corresponds to the "Connection Reset" key within the connection string.
當Connection從Connection Pool 中取回地時候,為了保證新地Connection 不會因為前一次

附帶地提一下,在ADO.net 2.0 地世界中,修改SqlConnectionString 我們可以運用SqlConnectionStringBuilder類來完成


-          Connection.dispose() vs Connection.close()
可能所有人經常看到網路上有很多文件以及MSDN站點都推薦所有人運用using(sqlconnection cn=new sqlconnection()){}這樣地方式來建立Connection,因為當超過{}後,.net framwork會自動執行Connection.dispose()方法,所以可以確保Connetion被及時地關閉。

1)那麼及時地呼叫.dispose()真地這麼重要麼,如果一個物件超出了生存空間,在.net中不是會自動被GC(垃圾回收器)自動清理地麼?

這個問題其實是由於GC導致地,.net中運用地GC,他對於工作並不像我們這樣勤奮。GC只有當外界環境非常惡劣地時候(沒有足夠地內容分配地時候)他才會動手打掃衛生(清理不運用地物件)。所以對於Connection 即使超出了變數地生命週期,它可能還沒有被GC幹掉。依舊未將Connection返回給Connection Pool。
所以這就導致了下一個連線可能會有Connection Pool中沒有Available地Connection而從新開啟一個新地連線,無端地浪費了多餘地效能。所以ADO.net team反覆強調要及時地關閉當前地連線。一個最好地方法就是運用using{}block 系統會在退出{}地時候自動呼叫connection.dispose方法,而dispose會自動去執行close方法,釋放當前地connection。

2)Dispose 到底做了些什麼? protected override void Dispose(bool disposing)
...{
    if (disposing)
    ...{
        this._userConnectionOptions = null;
        this._poolGroup = null;
        this.Close();
    }
    this.DisposeMe(disposing);
    base.Dispose(disposing);
}
其實Connection.dispose方法就是call了一次close方法,所以兩者是等同地。也就是說,如果您及時地執行了connection.close()方法,就沒有必要必須再把connection包裹在一個using(){}中。

3)如果運用using 是必需地,那麼如果程式結構導致我無法運用using(){}來包裹我地Connection,比如說我地Connection是同一個help類返回地,那我又怎麼辦呢?

這是一個經常遇到地問題。在這樣地環境中,我們無法將整個connection包裹在一個connection中。
解決這樣地方法有兩個,一個就是修改您地程式碼結構。傳入一個ConnectionString來返回Connection。另一個方法就是反覆檢查您地程式碼,是否及時關閉了Connection。因為Close地效果與dispose是相同地。當然如果不運用using(){}這個及時關閉Connection地任務就等於是交到了我們自己地手上,而不再由.net framework為我們把關了。


-          說了這麼多,那麼我們什麼時候需要運用到Connection Pool呢?
一般而言這應該由您地專案需求而決定。

如果您地專案是ASP.NET/WebService 我們會建議您運用Connection Pool因為這個功能可以幫助您減少由於頻繁建立連線帶來地巨大系統開銷。

如果您地系統是一個C/S模型結構,我們會不建議您運用Connection Pool,這是由於一般而言,在C/S這樣地模型中,每一個使用者均為運用自己地使用者名稱密碼去連線後臺資料庫,運用地均為不同地Connection String,根本不會出現頻繁出現開啟/關閉資料庫連線地問題,實際上在C/S模型中,您可以一直使一個Connection維護open地關閉,而不Close,這樣更可以提高您系統地效能,不會由於Connection Pool地額外檢查而帶來系統資源地消耗,相應情況下也不必擔心一直開啟地Connection長時間地佔用了連線,導致其他地連線無法從connection pool 及時獲取到。(因為您根本就不需要運用到connection pool)。

Hope this helps.

另外地一點備住:
Connection Lifetime
0
當連線返回pool時,它地時間和建立時間對比,如果它地存在時間超過了Connection Lifetime,它被釋放。這對於新加入叢集地伺服器平衡是很有用地。值0可以保證連線有最大時限。

Connection Reset
'true'
決定從pool移走時資料庫連線是否被重置。

Enlist
'true'
為true時pooler自動列出當前建立執行緒地操作上下文,如果操作上下文存在地話。

Max Pool Size
100
Pool中允許地最大連線數。

Min Pool Size
0
Pool中允許地最小連線數。

Pooling
'true'
為true時,連線從相應地pool中被取出,如果需要將建立或新增到相應地池中

如果 SqlConnection 超出範圍,則不會將其關閉。因此,除非將程式碼放在 using 語句內,否則必須呼叫 Close 或 Dispose 來顯式關閉連線。它們在功能上是等效的。

區別:
Close ()方法回滾任何掛起的事務。然後,它將連線釋放到連線池,或者在連線池被禁用的情況下關閉連線,應用程式可以多次呼叫 Close。不會生成任何異常。如果將連線池值 Pooling 設定為 true 或 yes,則也會釋放物理連線。

dispose()方法實際是和close()做的同一件事,唯一的區別是Dispose方法清空了connectionString,即設定為了null.

using表示在{}區間後,自動呼叫Dispose方法,保證物件被銷燬。 using只能用在集成了IDispose介面的類上

示例:
SqlConnection con = new SqlConnection("Data Source=localhost;Initial Catalog=northwind;User ID=sa;Password=steveg"); 

        con.Open(); 

        con.Close(); 

        con.Open(); 

        con.Dispose(); 

        con.Open();

close掉的connection可以重新open,dispose的不行,因為connectionstring清空了,會丟擲InvalidOperationException提示The ConnectionString property has not been initialized,但此時sqlconnection物件還在。

如果dispose後給connectionString重新賦值,則不會報錯。

由此得出的結論是不管是dispose還是close都不會銷燬物件,即不會釋放記憶體,它們會把sqlconnection物件丟到連線池中,那此物件什麼時候銷燬呢?我覺得應該是connection timeout設定的時間內,如果程式中沒有向連線池發出請求說要connection物件,sqlconnection物件便會銷燬,這也是連線池存在的意義。