簡體   English   中英

為什么這個 `using` 塊不釋放數據庫連接?

[英]Why does this `using` block not release the database connection?

編輯:@Jeroen Mostert 的評論效果很好:將Pooling=false添加到連接字符串。 正如他/她指出的那樣,我必須閱讀更多有關此添加對整體性能的影響的信息。

  • 朗:C#
  • 數據庫管理系統:Postgres

在下面的代碼中,有兩個連續的using塊(為了清楚起見,已刪除了很多);

  1. 在第一個using塊中,在刪除數據庫camsdb之前,我最后一次連接到數據庫並執行最終查詢。
  2. 查詢后,代碼退出第一個using塊,並應該關閉數據庫連接。
  3. 不幸的是,第二個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() 的調用;

正如你在這里看到的https://github.com/npgsql/npgsql/blob/01f3f97bf7500229f896fda1f56279da78894d12/src/Npgsql/NpgsqlConnection.cs

NpgsqlConnection 是 IDisposable。 當你為它調用 Dispose 時,它​​應該運行所有邏輯並關閉連接。 編寫一個會為其調用 Close 的 Wrapper 是不正確的。 當您這樣做時,它可能會出現隨機行為。

一個問題,如果 NpgsqlConnection 是 IDisposable,你真的需要你的 OWN 類,它本質上是一個包裝器(或代理)?

你,當然,已經知道了,但讓我們解決一些概念。

C#中的using是什么意思? 這意味着如果該類實現IDisposable ,則Dispose()方法將在 Object 實例生命周期結束時調用。

誰打開和關閉連接? 圖書館做到了。 您只需調用庫公開的某個類的實例的某個方法,並希望它為您完成這項工作。

為什么要保持連接? 大多數情況下,您確實希望與您的應用程序重用相同的連接,而且大多數情況下這很好,因為每個連接都會消耗資源。 這就是我們有連接池的原因。

你能做些什么來緩解你的問題? 您可以按照評論中的建議嘗試另一個庫。 或者看TemaTre 的回答,她/他看起來對那個特定的圖書館有很好的洞察力。

但也要注意你正在做一些不尋常的事情。 您確實有兩個連接(和兩個連接池?)。 一種用於“普通”用戶查詢數據庫。 管理員刪除數據庫的第二個連接。

我可以建議您首先將數據庫置於單用戶模式,並確保沒有其他連接,甚至是應用程序外部的連接。 您可以在刪除 DB 之前使用簡單的 SQL 命令來實現它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM