我是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);
}
}
}
}
答案 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组件挂钩,以便可以轻松地向用户显示其内容。