[英]Why does this `using` block not release the database connection?
編輯:@Jeroen Mostert 的評論效果很好:將Pooling=false
添加到連接字符串。 正如他/她指出的那樣,我必須閱讀更多有關此添加對整體性能的影響的信息。
在下面的代碼中,有兩個連續的using
塊(為了清楚起見,已刪除了很多);
using
塊中,在刪除數據庫camsdb
之前,我最后一次連接到數據庫並執行最終查詢。using
塊,並應該關閉數據庫連接。using
塊(應該刪除剛剛在第一個 using 塊中連接到的數據庫)未能刪除該數據庫。 PostgresException
表示“還有 1 個其他會話正在使用該數據庫。” 然而,當我注釋掉第一個using
塊時,第二個using
塊——履行其職責——毫無問題地刪除數據庫。 當代碼到達相關的花括號時,使用塊不是應該關閉連接嗎? 有沒有辦法強制垃圾收集器立即清理連接? (歡迎任何其他解決方案。)
private M_SqlConn m_sqlConn = null; // predefined
...
...
// *** using block no: 1 ***
using (m_sqlConn = new M_SqlConn("127.0.0.1", 5432, "camsdb", "my_user_name", "my_password")) {
// perform a last sql query
}
// *** using block no: 2 ***
using (m_sqlConn = new M_SqlConn("127.0.0.1", 5432, "postgres", "postgres", "admin_password")) {
if (this.DbDrop("camsdb")) {
FormWarn_ShowDialog(FormWarn.FormType.Info, "db dropped succesfully ...")
}
else {
FormWarn_ShowDialog(FormWarn.FormType.Error, "can not drop db !!!")
}
}
M_SqlConn
類作為一個整體如下(不想把頁面弄亂,所以我一開始沒有粘貼它):
using Npgsql;
using System;
using System.Text;
internal class M_SqlConn : IDisposable {
public Boolean connectionIsOpen = false;
private FormWarn formWarn = null;
private NpgsqlConnection sqlConnection = null;
private String hostAddress = "";
private String portNumber = "";
private String dbName = "";
private String roleName = "";
private String password = "";
internal M_SqlConn(String hostAddress, String portNumber, String dbName, String roleName, String password) {
this.hostAddress = hostAddress;
this.portNumber = portNumber;
this.dbName = dbName;
this.roleName = roleName;
this.password = password;
this.ConnectionOpen();
return;
}
private void ConnectionOpen() {
StringBuilder exceptionString = new StringBuilder(String.Empty);
NpgsqlConnectionStringBuilder connectionStringBuilder = new NpgsqlConnectionStringBuilder {
Host = this.hostAddress,
Port = Convert.ToInt32(this.portNumber),
Database = this.dbName,
Username = this.roleName
};
this.sqlConnection = new NpgsqlConnection(connectionStringBuilder.ToString() + $";Password={this.password}");
try {
this.sqlConnection.Open();
if (this.sqlConnection.State == System.Data.ConnectionState.Open) {
this.connectionIsOpen = true;
}
}
catch (PostgresException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Postgres Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (NpgsqlException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Npgsql Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (Exception e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"General Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
if (!this.connectionIsOpen) {
this.FormWarn_ShowDialog(FormWarn.FormType.Error, exceptionString.ToString());
}
return;
}
private void ConnectionClose() {
StringBuilder exceptionString = new StringBuilder(String.Empty);
if (this.sqlConnection != null) {
try {
this.sqlConnection.Close();
this.connectionIsOpen = false;
}
catch (PostgresException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Postgres Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (NpgsqlException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Npgsql Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (Exception e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"General Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
if (this.connectionIsOpen) {
this.FormWarn_ShowDialog(FormWarn.FormType.Error, exceptionString.ToString());
}
else {
this.sqlConnection.Dispose();
this.sqlConnection = null;
}
}
return;
}
public Boolean SqlCommandExecuteNonQuery(String sqlString) {
Boolean commandStatus = false;
StringBuilder exceptionString = new StringBuilder(String.Empty);
using (NpgsqlCommand sqlCommand = new NpgsqlCommand(sqlString, this.sqlConnection)) {
try {
sqlCommand.ExecuteNonQuery();
commandStatus = true;
}
catch (PostgresException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Postgres Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (NpgsqlException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Npgsql Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (Exception e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"General Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
};
if (!commandStatus) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Sql string is:{Environment.NewLine}" +
$"{sqlString}{Environment.NewLine}");
this.FormWarn_ShowDialog(FormWarn.FormType.Error, exceptionString.ToString());
}
return (commandStatus);
}
public Int32 SqlCommandExecuteScalar(String sqlString) {
Int32 count = -1;
StringBuilder exceptionString = new StringBuilder(String.Empty);
using (NpgsqlCommand sqlCommand = new NpgsqlCommand(sqlString, this.sqlConnection)) {
try {
Int32 countTmp = Convert.ToInt32(sqlCommand.ExecuteScalar());
count = countTmp;
}
catch (PostgresException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Postgres Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (NpgsqlException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Npgsql Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (Exception e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"General Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
};
if (count == -1) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Sql string is:{Environment.NewLine}" +
$"{sqlString}{Environment.NewLine}");
this.FormWarn_ShowDialog(FormWarn.FormType.Error, exceptionString.ToString());
}
return (count);
}
public NpgsqlDataReader SqlCommandExecuteQuery(String sqlString) {
NpgsqlDataReader dataReader = null;
StringBuilder exceptionString = new StringBuilder(String.Empty);
using (NpgsqlCommand sqlCommand = new NpgsqlCommand(sqlString, this.sqlConnection)) {
try {
dataReader = sqlCommand.ExecuteReader();
}
catch (PostgresException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Postgres Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (NpgsqlException e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Npgsql Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
catch (Exception e) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"General Exception{Environment.NewLine}" +
$"{e.ToString()}{Environment.NewLine}");
}
};
if (exceptionString.Length > 0) {
exceptionString.AppendLine(
$"{Environment.NewLine}" +
$"Sql string is:{Environment.NewLine}" +
$"{sqlString}{Environment.NewLine}");
this.FormWarn_ShowDialog(FormWarn.FormType.Error, exceptionString.ToString());
}
return (dataReader);
}
private void FormWarn_ShowDialog(FormWarn.FormType formType, String msg) {
using (this.formWarn = new FormWarn(formType, msg)) {
this.formWarn.ShowDialog();
}
this.formWarn.Dispose();
this.formWarn = null;
return;
}
public void Dispose() {
if (this.formWarn != null) {
this.formWarn.Dispose();
this.formWarn = null;
}
if (this.connectionIsOpen) {
this.ConnectionClose();
}
this.hostAddress = "";
this.portNumber = "";
this.dbName = "";
this.roleName = "";
this.password = "";
return;
}
}
我試過@Panagiotis 的伎倆,但沒有奏效。 將if
塊更改為以下內容,但這次程序停止給出“ System.NullReferenceException:'未將對象引用設置為對象的實例。' ”
if (this.sqlConnection.State == System.Data.ConnectionState.Open) {
this.sqlConnection.Dispose(); // exception here
this.sqlConnection = null;
}
您沒有為 sqlConnection 調用 dispose。 請取消注釋。
this.sqlConnection.Dispose();
並刪除您的方法 CloseConnection() 的調用;
NpgsqlConnection 是 IDisposable。 當你為它調用 Dispose 時,它應該運行所有邏輯並關閉連接。 編寫一個會為其調用 Close 的 Wrapper 是不正確的。 當您這樣做時,它可能會出現隨機行為。
一個問題,如果 NpgsqlConnection 是 IDisposable,你真的需要你的 OWN 類,它本質上是一個包裝器(或代理)?
你,當然,已經知道了,但讓我們解決一些概念。
C#中的using
是什么意思? 這意味着如果該類實現IDisposable
,則Dispose()
方法將在 Object 實例生命周期結束時調用。
誰打開和關閉連接? 圖書館做到了。 您只需調用庫公開的某個類的實例的某個方法,並希望它為您完成這項工作。
為什么要保持連接? 大多數情況下,您確實希望與您的應用程序重用相同的連接,而且大多數情況下這很好,因為每個連接都會消耗資源。 這就是我們有連接池的原因。
你能做些什么來緩解你的問題? 您可以按照評論中的建議嘗試另一個庫。 或者看TemaTre 的回答,她/他看起來對那個特定的圖書館有很好的洞察力。
但也要注意你正在做一些不尋常的事情。 您確實有兩個連接(和兩個連接池?)。 一種用於“普通”用戶查詢數據庫。 管理員刪除數據庫的第二個連接。
我可以建議您首先將數據庫置於單用戶模式,並確保沒有其他連接,甚至是應用程序外部的連接。 您可以在刪除 DB 之前使用簡單的 SQL 命令來實現它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.