【问题标题】:Invalid attempt to call Read when reader is closed. (C#)阅读器关闭时调用 Read 的尝试无效。 (C#)
【发布时间】:2015-10-15 20:17:04
【问题描述】:

我创建了一种从表中获取数据的方法,但它给了我标题中所述的错误。在这里发帖之前,我已经从包括 stackoverflow 在内的各种网站上阅读了很多方法,但它对我没有帮助。

下面是我的方法。

    public SqlDataReader GetCustomer()
    {
        SqlDataReader reader = null;
        _conn = new SqlConnection(connString);
        string sqlQuery = @"SELECT CustName, CustNationality FROM Customer";
        using (_conn)
        {
            using (SqlCommand cmd = new SqlCommand(sqlQuery, _conn))
            {
                _conn.Open();
                reader = cmd.ExecuteReader();
                _conn.Close();
            }
        }
        return reader;
    }

我将方法称为;

        SqlDataReader reader = null;
        cmboBoxClient.Items.Add("");
        reader = connect.GetCustomer() as SqlDataReader;
        while (reader.Read())
        {
            cmboBoxClient.Items.Add(reader["CustName"] + " " + reader["CustNationality"]);
        }

但是给了我错误,我尝试了 IEnumerable 方法,但是它给了我转换/转换错误。即 IDataReader 到 SqlDataReader。 后来我读到这种方法是不可能的。

请给我建议。

【问题讨论】:

  • GetCustomer 方法返回客户实例,而不是返回阅读器

标签: c# sql sql-server


【解决方案1】:

您在返回阅读器之前关闭连接。您应该在使用数据库游标时保持连接打开。因此,返回阅读器不是最佳选择,因为您无法在调用者使用连接时处理连接。我建议你创建强类型类Customer

public class Customer
{
    public string Name { get; set; }
    public string Nationality { get; set; }
}

并在您的 GetCustomers 方法中填写客户列表(是的,您的查询返回许多客户,而不是单个客户):

public IEnumerable<Customer> GetCustomers()
{
    string sqlQuery = @"SELECT CustName, CustNationality FROM Customer";

    using (var conn = new SqlConnection(connString))        
    using (SqlCommand cmd = new SqlCommand(sqlQuery, conn))
    {
        conn.Open();
        var reader = cmd.ExecuteReader();

        while(reader.Read())
        {
            yield return new Customer {
                Name = (string)reader["CustName"],
                Nationality = (string)reader["CustNationality"]
            };
        }
    }
}

这将允许您在枚举结束时自动关闭连接,并使调用者代码远离数据库详细信息:

cmboBoxClient.Items.Add("");

foreach(var customer in connect.GetCustomers())
    cmboBoxClient.Items.Add(String.Format("{0} {1}",customer.Name, customer.Nationality));

顺便说一句,Dapper 可以为您完成所有查询和映射。所以代码看起来像

public IEnumerable<Customer> GetCustomers()
{
    using (var conn = new SqlConnection(connString))
    {                
        conn.Open();
        return conn.Query<Customer>("SELECT CustName, CustNationality FROM Customer");
    }
}

是的,这就是你所需要的。

【讨论】:

  • “因为你不能在调用者使用连接时释放连接”,是的,你可以,如果你在释放阅读器时执行cmd.ExecuteReader(CommandBehavior.CloseConnection),它会关闭底层连接.虽然它不是“处理”连接,但处理连接所做的唯一事情就是关闭它(加上一些内部变量)
  • 这是对 OP 错误的解释 - 目前他在调用者使用连接时关闭了连接。但无论如何,感谢您指向CommandBehavior 选项
  • 啊,对不起,误解了声明。
【解决方案2】:

当你关闭连接时,阅读器不再有效。

您可以在关闭连接和处理命令之前将您的阅读逻辑(您拥有的循环)移动到其他方法(即GetCustomer)中。

另一种解决方案是使用这样的回调:

public void GetCustomer(Action<SqlDataReader> callback)
{
    var connString = "connection_string";

    SqlDataReader reader = null;
    var _conn = new SqlConnection(connString);
    string sqlQuery = @"SELECT CustName, CustNationality FROM Customer";
    using (_conn)
    {
        using (SqlCommand cmd = new SqlCommand(sqlQuery, _conn))
        {
            _conn.Open();
            using(reader = cmd.ExecuteReader())
            {
                callback(reader);
            }
        }
    }
}

然后像这样使用它:

connect.GetCustomer((reader =>
{
    while (reader.Read())
    {
        cmboBoxClient.Items.Add(reader["CustName"] + " " + reader["CustNationality"]);
    }
}));

这使您可以使GetCustomer 方法更通用。您可以多次重复使用它,并且每次都可以以不同的方式使用阅读器。

您可能希望为该方法使用更好的名称。例如,ReadCustomers

【讨论】:

    猜你喜欢
    • 2013-12-26
    • 1970-01-01
    • 1970-01-01
    • 2013-06-19
    • 1970-01-01
    相关资源
    最近更新 更多