【问题标题】:MySql.Data.MySqlClient.MySqlException : 'There is already an open DataReader associated with this Connection which must be closed first.'MySql.Data.MySqlClient.MySqlException:'已经有一个打开的 DataReader 与此连接关联,必须先关闭。'
【发布时间】:2018-06-01 20:35:56
【问题描述】:
MySqlCommand Sql1 = new MySqlCommand("SELECT * FROM animal WHERE idAnimal ='" + label1.Text + "'", Connection);

MySqlDataReader dr1;
dr1 = Sql1.ExecuteReader();

while (dr1.Read())
{
    String idAnimal = dr1["idAnimal"].ToString();

    MySqlCommand Sql2 = new MySqlCommand("SELECT * FROM town WHERE id ='" + idAnimal + "'", Connectio);
    MySqlDataReader dr2;
    dr2 = Sql2.ExecuteReader();

    while (dr2.Read())
    {
        dataGridView1.Rows.Add(dr2["number"], dr2["name"]);
    }

    dr2.Close();
}

dr1.Close();
Connection.Close();

【问题讨论】:

  • 为什么不先将所有 idAnimal 值读入一个数组,然后关闭第一个读取器?此外,当您只使用单个字段时,“SELECT *”没有任何意义......并且该字段是您作为条件传入的字段。
  • 啊! sql注入漏洞,烧死我们了!

标签: c# mysql


【解决方案1】:

解决此问题的最佳方法是使用 JOIN(并在我们处理该问题时修复 HUGE sql 注入漏洞):

string sql = "SELECT t.number, t.name FROM animal a INNER JOIN town t ON t.ID = a.idAnimal WHERE a.idAnimal= @idAnimal";

using (var cn = new MySqlConnection("connection string here"))
using (var cmd = new MySqlCommand(sql, cn))
{
    cmd.Parameters.Add("@idAnimal", MySqlDbType.Int32).Value = int.Parse(label1.Text);

    cn.Open();
    using (var dr = cmd.ExecuteReader())
    {
        while(dr.Read())
        {
            dataGridView1.Rows.Add(dr["number"], dr["name"]);
        }
        dr.Close();
    }
}

此外,您可能应该查看 数据绑定 以将这些结果连接到您的网格,而不是手动添加行。这会让你编写这样的代码:

string sql = "SELECT t.number, t.name FROM animal a INNER JOIN town t ON t.ID = a.idAnimal WHERE a.idAnimal= @idAnimal";
using (var cn = new MySqlConnection("connection string here"))
using (var cmd = new MySqlCommand(sql, cn))
{
    cmd.Parameters.Add("@idAnimal", MySqlDbType.Int32).Value = int.Parse(label1.Text);
    cn.Open();
    using (var dr = cmd.ExecuteReader())
    {
        dataGridView1.DataSource = dr;
        dr.Close();
    }
}

但如果你真的想知道如何让两个 DataReader 一起活动,你可以通过两个连接对象来做到这一点:

using (var cn1 = new MySqlConnection("connection string here"))
using (var sql1 = new MySqlCommand("SELECT * FROM animal WHERE idAnimal = @idAnimal", cn1))
{
    sql1.Parameters.Add("@idAnimal", MySqlDbType.Int32).Value = int.Parse(label1.Text);
    cn1.Open();
    using (var dr1 = sql1.ExecuteReader())
    {    
        while (dr1.Read())
        {
            String idAnimal = dr1["idAnimal"].ToString();

            using (var cn2 = new MySqlConnection("connection string here"))
            using (var sql2 = new MySqlCommand("SELECT * FROM town WHERE id = @idAnimal", cn2))
            {
                cn2.Parameters.Add("@idAnimal", MySqlDbType.Int32).Value = int.Parse(idAnimal);
                cn2.Open();
                using(var dr2 = sql2.ExecuteReader())
                {
                    while (dr2.Read())
                    {
                        dataGridView1.Rows.Add(dr2["number"], dr2["name"]);
                    }
                    dr2.Close();
                }
            }
        }
        dr1.Close();
    }
}

但请注意,这是 JOIN + DataBinding 选项的两倍多代码。

另请注意,在 ADO.Net 提供程序中糟糕的做法是保留一个数据库连接以便在您的应用程序中重复使用。除了限制您同时使用多个数据库查询的能力之外,正如我们在此处看到的,ADO.Net 使用了一个名为Connection Pooling 的功能,并且重复使用相同的连接对象会干扰这一点。在大多数情况下,最好创建一个新的连接对象,然后简单地重新使用连接字符串。

【讨论】:

  • 终于搞定了!! “同时进行多个数据库查询”这就是为什么不重用连接对象的原因,但是就连接池而言,我的理解是连接一旦关闭就会返回到池中。它如何干扰池化?
  • 软的。这非常简单:不要在整个应用程序中重复使用同一个对象。创建新对象并不难,ADO.Net 使您的应用程序的这个过程比重复使用更快
  • 但是重复使用同一个对象会干扰池化,因为陈旧的连接可能会失效或被丢弃。使用重复使用的连接对象,您最终可能会尝试重新打开已处置的项目,这将导致应用程序出现问题。
  • 更快。太好了,我不知道。我想我的观点非常短视,因为我的数据层都是我的。谢谢。
  • 如果您将Label1.Text 值直接替换到sql 字符串中,则没有数据层。
【解决方案2】:

您不能同时在两个命令中使用相同的“连接”变量。如果您想在第一个的读取中打开另一个连接,只需创建第二个。

【讨论】:

    【解决方案3】:

    您对 DataReader 和 ExecuteNonQuery 使用相同的连接。根据 MSDN,不支持您必须为每个 datareader 创建单独的连接

    【讨论】:

    • 虽然这个说法是正确的,但是如果你想参考MSDN,你应该提供一个指向具体文章或资源的链接。
    猜你喜欢
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-08
    • 1970-01-01
    • 2011-08-29
    • 2020-09-25
    • 2011-12-26
    相关资源
    最近更新 更多