读取器关闭时无效尝试调用Read。 (C#)

时间:2015-10-15 20:17:05

标签: c# sql sql-server

我创建了一个从表中获取数据的方法,但它给出了标题中所述的错误。在发布之前,我已经从各种网站上阅读了很多方法,包括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。 后来我读到这种方法是不可能的。

请建议我。

2 个答案:

答案 0 :(得分:4)

您在返回阅读器之前关闭连接。在使用数据库游标时应保持连接打开。因此,返回阅读器不是最好的选择,因为在调用者使用它时你不能处理连接。我建议你创建一个强类型的类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));

BTW Dapper可以为您执行所有查询和映射。所以代码看起来像

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

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

答案 1 :(得分:1)

当您关闭连接时,阅读器将无法再使用。

在关闭连接之前和处理命令之前,您可以在其他方法(即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