0

我目前有一个小应用程序向服务器发送许多不同的 MySQL 查询。我的想法是将连接、查询和读取包装到一个仅将实际查询作为参数的函数。

这是我得到的:

 public static MySqlDataReader mySqlRead(string cmdText)
    {

        string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";

        MySqlConnection conn = new MySqlConnection(connString);
        MySqlCommand command = conn.CreateCommand();

        command.CommandText = cmdText;

        try
        {
            conn.Open();
            MySqlDataReader reader = command.ExecuteReader();
            return reader;
        }

        catch (MySqlException)
        {
            throw;
        }

    }

我在这里连接并发送查询:

private void btnLogin_Click(object sender, EventArgs e)
    {
        string username = txtLogin.Text;
        string password = ORFunc.GetMD5Hash(txtPassword.Text);

        MySqlDataReader orRead = ORFunc.mySqlRead("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");
        while (orRead.Read())
        {
            MessageBox.Show(orRead["id"].ToString());
        }
    }

像魅力一样工作......但是,正如您在上面看到的,连接永远不会关闭。当我在 .ExecuteReader() 后面添加 conn.Close() 时,阅读器是空的,返回后的所有内容当然都是无用的。

也许这是一个愚蠢的问题,但我对 C# 相当陌生,所以请大方,任何提示都表示赞赏。

干杯,

Primus

4

2 回答 2

2

I had a similar problem in JAVA recently, but I think the same will work for you. Essentially, you can create a class that represents a "SqlCall" object (or something). The class would have accessible members including the connection and the results. The ctor for the class would take in your query text.

Then, all you would have to do is create a new instance of that class, run the query in a method in that class (which would set and/or return the results), GET the results, and then when you are done, call close() on your class (which would then have to be coded such that it closes the connection held internally).

Technically, a better way to do this is to EXTEND the connection class itself, but as you are new to C#, I will not go into the details of doing so.

As I was writing the code below, I realized I may have not actually answered your question. But there's no point in backing out now, so here's what I have:

public class SqlCall {

    private static connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
    private MySqlConnection conn;
    private MySqlCommand command;
    private MySqlDataReader reader;

    public SqlCall(String query) {

        conn = new MySqlConnection(connString);
        command = conn.CreateCommand();
        command.CommandText = query;

    }

    public MySqlDataReader execute() throws Exception {
        conn.Open();
        reader = command.ExecuteReader();
        return reader;
    }

    public void close() {
        reader.close();
        conn.close();
    }

}

Your login code would be:

private void btnLogin_Click(object sender, EventArgs e) {
    string username = txtLogin.Text;
    string password = ORFunc.GetMD5Hash(txtPassword.Text);

    SqlCall sqlcall = new SqlCall("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");

    try {
        MySqlDataReader orRead = sqlcall.execute();
        while (orRead.Read())
        {
            MessageBox.Show(orRead["id"].ToString());
        }
        sqlcall.close();
    } catch (Exception ex) {
        // dostuff
    }
}

The point is, unless you copy the data into a new datatable at the very beginning, you'll have to keep the connection open.

On a separate note, YOUR CODE IS PRONE TO SQL INJECTION. Don't know what that is? An example: if I said my username was ';DROP TABLE orUsers;--, then your entire user database would be gone. Look into stored procedures if you want a (very healthy) way around this.

于 2013-06-26T19:42:58.430 回答
1

您遇到了困难,因为您的想法与在 NET Framework 中连接到数据库的程序所期望的模式相违背。
通常,在这种模式下,你有一个方法

INITIALIZE/OPEN/USE/CLOSE/DESTROY 

ADO.NET 对象连接到提取或更新数据所需的工作

此外,您的代码有一个称为 Sql Injection 的严重问题(请参阅这个著名的解释),因为当您连接字符串以形成命令文本时,您无法防御试图攻击您的数据库的恶意用户

private void btnLogin_Click(object sender, EventArgs e)
{
    string username = txtLogin.Text;
    string password = ORFunc.GetMD5Hash(txtPassword.Text);

    MySqlParameter p1 = new MySqlParameter("@uname", username);
    MySqlParameter p2 = new MySqlParameter("@pass", pass);
    string cmdText = "SELECT * FROM orUsers WHERE username = @uname AND pass = @pass"
    DataTable dt = ORFunc.GetTable(cmdText, p1, p2);
    foreach(DataRow r in dt.Rows)
    {
       Console.WriteLine(r["ID"].ToString());
    }
}

public static DataTable GetTable(string cmdText, params MySqlParameter[] prms)
{
    string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
    // This is the INITIALIZE part
    using(MySqlConnection conn = new MySqlConnection(connString))
    using(MySqlCommand command = new MySqlCommand(cmdText, conn))
    {
         // OPEN
         conn.Open();
         DataTable dt = new DataTable();
         command.Parameters.AddRange(prms);

         // USE
         MySqlDataReader reader = command.ExecuteReader();
         dt.Load(reader);
         return dt;
    } // The closing brace of the using statement is the CLOSE/DESTROY part of the pattern
}

当然,这是一个通用示例,在我的实际工作中,我不经常使用这些通用方法,而是更喜欢编写专门的数据访问代码,将所需的基础对象返回给上层代码

于 2013-06-26T19:48:40.217 回答