如何将SqlDataReader复制到数组中然后循环遍历数组?

时间:2014-12-28 00:35:42

标签: c# mysql arrays

我是C#的新手所以是的,这应该是一个非常简单的问题,但我似乎无法找到答案。

我有一个查询数据库的方法。

我在这里尝试做的是通过方法之外的数据处理循环。

public MySqlDataReader getDataSet(string query)
{
    MySqlDataReader dataset = null;
    MySqlConnection conn = new MySqlConnection(conn_string);

    if (startConnection(conn) == true)
    {
        MySqlCommand cmd = new MySqlCommand(query, conn);
        dataset = cmd.ExecuteReader();
        closeConnection(conn);
    }
    return dataset;
}

我能做的就是在closeConnection(conn);行之前写一个while循环并处理数据。但是,我不想在这个方法中做这个,我想在我的代码中的其他地方做。

在我的一个表单中,我想在负载上读取数据库,所以这是我尝试做的事情

    public newDepartment()
    {
        InitializeComponent();

        inputDepartmentName.Text = "Hi";

        dbConnetion db = new dbConnetion();
        MySqlDataReader ds = db.getDataSet("SELECT name FROM test;");

        while (ds.Read())
        {

        //Do Something

        }

    }

我遇到的问题是我收到错误Invalid attempt to Read when reader is closed

我相信我得到这个问题,因为我关闭连接,然后我试图阅读它。所以我需要做的是从查询中读取数据并将其放入数组中,然后遍历数组并以不同的形式处理数据。

如何解决此问题?如果我的想法很好,那么我如何将数据复制到一个数组中?如何循环数组呢?

这是完整的课程

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
using System.Windows.Forms;


namespace POS
{
    public class dbConnetion
    {
        //private OdbcConnection conn; 
        private readonly string mServer;
        private readonly string mDatabase;
        private readonly string mUid;
        private readonly string mPassword;
        private readonly string mPort;
        private readonly string conn_string;
        public dbConnetion()
        {
            mServer = "localhost";
            mDatabase = "pos";
            mUid = "root";
            mPassword = "";
            mPort = "3306";

            conn_string = String.Format("server={0};user={1};database={2};port={3};password={4};", mServer, mUid, mDatabase, mPort, mPassword);



        }

        //Start connection to database
        private bool startConnection(MySqlConnection mConnection)
        {

            try
            {
                mConnection.Open();
                return true;
            }
            catch (MySqlException ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK);
                return false;
            }

        }


        //Close connection
        private bool closeConnection(MySqlConnection mConnection)
        {
            try
            {
                mConnection.Close();
                return true;
            }
            catch (MySqlException ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }

        public MySqlDataReader getDataSet(string query)
        {
            MySqlDataReader dataset = null;
            MySqlConnection conn = new MySqlConnection(conn_string);

            if (startConnection(conn) == true)
            {
                MySqlCommand cmd = new MySqlCommand(query, conn);
                dataset = cmd.ExecuteReader();
                closeConnection(conn);
            }
            return dataset;
        }


        public void processQuery(string strSQL, List<MySqlParameter> pars)
        {
            MySqlConnection conn = new MySqlConnection(conn_string);


            if (startConnection(conn) == true)
            {
                MySqlCommand cmd = new MySqlCommand(strSQL, conn);

                foreach (MySqlParameter param in pars)
                {
                    cmd.Parameters.Add(param);
                }

                cmd.ExecuteNonQuery();
                closeConnection(conn);
            }
        }
    }
}

2 个答案:

答案 0 :(得分:3)

将记录放入数组会破坏使用datareader的最佳功能:您只需要一次为一条记录分配内存。尝试做这样的事情:

public IEnumerable<T> getData<T>(string query, Func<IDataRecord, T> transform)
{       
    using (var conn = new MySqlConnection(conn_string))
    using (var cmd = new MySqlCommand(query, conn))
    {
        conn.Open();

        using (var rdr = cmd.ExecuteReader())
        {
           while (rdr.Read())
           {
               yield return transform(rdr);
           }
        }
    }
}

虽然我在这里,但是这个代码和原始版本存在非常严重的安全漏洞。像这样的方法只接受一个查询字符串,没有单独的参数机制,迫使你编写的代码非常容易受到sql注入攻击。 processQuery()方法已经解决了这个问题,所以让我们扩展getDataset()以避免这个安全问题:

public IEnumerable<T> getData<T>(string query, List<MySqlParameter> pars, Func<IDataRecord, T> transform)
{      
    using (var conn = new MySqlConnection(conn_string))
    using (var cmd = new MySqlCommand(query, conn))
    {
        if (pars != null) 
        {
            foreach(MySqlParameter p in pars) cmd.Parameters.Add(p);
        }

        conn.Open();

        using (var rdr = cmd.ExecuteReader())
        {
           while (rdr.Read())
           {
               yield return transform(rdr);
           }
        }
    }
}

好多了。现在我们不必编写只是要求被黑客入侵的代码。以下是您的newDepartment()方法现在的样子:

public newDepartment()
{
    InitializeComponent();

    inputDepartmentName.Text = "Hi";

    dbConnetion db = new dbConnetion();
    foreach(string name in db.getDataSet("SELECT name FROM test;", null, r => r["name"].ToString() ))
    {
       //Do Something
    }
}

关于此代码的一件事是使用委托来提供创建强类型对象的方法。这样做是因为数据引用器的工作方式:如果在每次迭代时都没有创建新对象,那么您正在处理同一个对象,这可能会产生不良结果。在这种情况下,我不知道你正在使用什么类型的对象,所以我只是根据你的SELECT查询所做的事情使用了一个字符串。


基于单独的讨论,这里是一个为更复杂的结果集调用它的例子:

foreach(var item in db.getDataSet(" long query here ", null, r =>
             new columnClass()
             {
                firstname = r["firstname"].ToString(),
                lastname = r["lastname"].ToString(),
                //...
             }
          ) )
{
    //Do something
}

答案 1 :(得分:0)

由于您是.Net的新手,我想我指出ADO.Net中有两层数据库访问。您正在使用数据阅读器方式,所有这些只是在线阅读查询。这是最低级别的访问,将为您提供最佳性能,但它更多的工作。对于大多数连接类型,您只能执行一个命令或每个连接有一个活动数据读取器(并且在您正在执行查询之前无法关闭连接。)

另一种形式是离线数据适配器,只需要一些不同的代码,但通常更容易使用。

public DataTable getDataSet(string query)
{
    MySqlConnection conn = new MySqlConnection(conn_string);

    if (startConnection(conn) == true)
    {
        MySqlDataAdapter adapter = new MySqlDataAdapter(query, conn);
        DataTable table = new DataTable();
        adapter.Fill(table);
        closeConnection(conn);
        return table;
    }
    return null;
}

这将导致您获得一个DataTable,其中包含与查询结果相对应的列和行(如果您希望稍后将更改发回数据库,请查看命令构建器,但为此您需要保持连接打开。

使用数据适配器的一个好处是,它将确定正确的数据类型应该是什么,因此您在从数据读取器读取数据时不必担心无效的强制转换异常。

有人指出,虽然你需要将所有数据读入内存,如果你处理大量内存,这可能是一个问题。当你开始处理大量记录时,DataTable类也非常慢。最后,DataTable和DataSet类通常也很好地与.Net中的UI组件挂钩,以便可以轻松地向用户显示其内容。