【问题标题】:Having trouble with: The connection was not closed. The connection's current state is open. - SQL Server & C#遇到问题:连接未关闭。连接的当前状态是打开的。 - SQL Server & C#
【发布时间】:2020-01-30 18:35:31
【问题描述】:

我目前正在处理学校管理系统的登录表单。问题是,当我尝试登录时,出现错误:

System.InvalidOperationException:连接未关闭。连接的当前状态是打开的

它说错误出现在代码的第 30 行,但我似乎找不到解决方法。

这是发生错误的方法的代码:

public void LoginTeacher()
{
        try
        {
            command = new SqlCommand("TeacherLogin", connection);
            command.CommandType = CommandType.StoredProcedure;

            connection.Open(); // This is the 30th line. 

            command.Parameters.AddWithValue("@username", Txt_User.Text);
            command.Parameters.AddWithValue("@password", Txt_Pass.Text);

            SqlDataReader dataReader = command.ExecuteReader();

            if (dataReader.Read())
            { 
                    TeacherDash teacherDash = new TeacherDash();
                    this.Hide();
                    teacherDash.lblusertype.Text = dataReader[1] + " " + dataReader[2].ToString();
                    teacherDash.ShowDialog();
                    this.Close();
                }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        finally
        {
            connection.Close();
        }
}

显示该错误后,立即有另一个显示:

System.InvalidOperationException: 阅读器关闭时调用 CheckDataIsReady 的尝试无效

并指向第 71 行,如下所示:

public void Login()
{
        try
        {
            command = new SqlCommand("SP_USER_LOGIN", connection);
            command.CommandType = CommandType.StoredProcedure;

            connection.Open();

            command.Parameters.AddWithValue("@user", Txt_User.Text);
            command.Parameters.AddWithValue("@pass", Txt_Pass.Text);

            SqlDataReader dataReader = command.ExecuteReader();

            if (dataReader.Read())
            {
                LoginTeacher();

                if (dataReader[10].Equals("Admin"))
                {
                    AdminDash adminDash = new AdminDash();
                    this.Hide();
                    adminDash.lblusertype.Text = dataReader[1] + " " + dataReader[2].ToString();
                    adminDash.ShowDialog();

                    this.Close();
                }

之后还有更多代码,但我觉得它不相关,因为它是同一件事,但用户类型不同。

提前致谢!

【问题讨论】:

  • 您不应该像那样共享connection,在使用它的方法中创建它并将其放在using 块中
  • 请粘贴exact 错误,不要解释它或给我们摘录。但是,是的,JSteward 是对的,您不应该像那样共享连接对象。不要太担心会频繁打开和关闭连接 - ADO.NET 使用 connection pooling,因此实际上您不会像代码看起来那样打开与数据库的物理连接。
  • ADO.NET 在幕后为您处理连接池。最佳做法是为要执行的每个 sql 语句/查询创建一个新连接。通过将连接实例包装在using 块中来确保它们被释放/关闭。这是您的主要问题,因为您的代码似乎试图共享以前关闭的单个连接。
  • 在不相关的注释中,不要在您的存储过程名称前加上 sp_。这是 sql server 中内部对象的保留命名约定。另见stackoverflow.com/a/20530262/1260204

标签: c# sql-server ado.net


【解决方案1】:

您可以尝试将 TeacherLogin() 方法更改为以下内容:

public void TeacherLogin()
{
    try
    {
        using(SqlConnection con = new SqlConnection("connection string"))
        {
            using(SqlCommand cmd = new SqlCommand("TeacherLogin"))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@username", Txt_User.Text);
                cmd.Parameters.AddWithValue("@password", Txt_Pass.Text);
                cmd.Connection = con;
                con.Open();
                using(SqlDataReader dr = cmd.ExecuteReader())
                {
                    while(dr.Read())
                    {
                        TeacherDash teacherDash = new TeacherDash();
                        this.Hide();
                        teacherDash.lblusertype.Text = string.Format("{0} {1}", dr[1], dr[2]);
                        teacherDash.ShowDialog();
                    }
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

没有必要使用 finally{} 来关闭连接,因为它全部包装在 using() 块中,当代码离开块时,它将自行关闭和处理。我总是建议以这种方式使用 SQL 连接和命令,因为不这样做会导致连接打开而导致问题。

【讨论】:

  • 我看到了语法错误以及可以用来帮助调试运行时错误的良好异常信息的吞咽。
  • @Igor 这将教我用记事本而不是适当的工具写出这些东西!我现在已经更正了示例中的一些内容。异常信息可以在 catch 中捕获并由发布者自己的代码处理,或者在他们认为合适的情况下完全忽略,我猜。
【解决方案2】:

数据库对象需要关闭和处置。将它们保留在使用它们的方法的本地可以确保发生这种情况。使用积木为您解决这个问题。

我使用了DataTable 而不是使用阅读器进行测试,因为只要阅读器在使用中,连接就必须保持打开状态。在尽可能短的时间内打开和关闭连接很重要。

请不要使用.AddWithValue。见http://www.dbdelta.com/addwithvalue-is-evil/https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/ 还有一个: https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications 这是另一个 https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html 当然,您必须检查数据库的真实数据类型和字段大小才能使用正确的 .Add 方法。

    public void LoginTeacher()
    {
        DataTable dt = new DataTable();
        using (SqlConnection cn = new SqlConnection("your connection string"))
        using (SqlCommand cmd = new SqlCommand("TeacherLogin", cn))
        { 
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add("@username",SqlDbType.VarChar,100 ).Value = Txt_User.Text;
            cmd.Parameters.Add("@password",SqlDbType.VarChar, 100 ).Value =Txt_Pass.Text;
            cn.Open();
            dt.Load(cmd.ExecuteReader());
        } //Your connection and command are both disposed
        if (dt.Rows.Count > 0)
        {
            TeacherDash teacherDash = new TeacherDash();
            teacherDash.lblusertype.Text = $"{dt.Rows[0][1]} {dt.Rows[0][2]}";
            teacherDash.ShowDialog();
            Close();
        }
        else
            MessageBox.Show("Sorry, login failed");
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多