MySQL 连接抛出空引用

MySQL connection throws null reference

我的 MySQL 连接抛出空引用,尽管这段代码在一年前运行良好。
调试器指示异常的行包含
"connection = new MySqlConnection(connectionString);":

DBConnect MySqlConnection = new DBConnect();

string[] resultArray = MySqlConnection.GetValidUser(
    "tbl_user",
    tbEmail.Text, 
    tbPassword.Text
);

//THROWS null reference exception in method 'private bool OpenConnection()'
//A first chance exception of type 'System.ArgumentException' occurred in System.Data.dll

这是我的 DBConnect class:

class DBConnect
{
    private MySqlConnection connection;
    private string server;
    private string database;
    private string uid;
    private string password;

    public DBConnect()
    {
        server = "x.x.x.x";
        database = "...";
        uid = "...";
        password = "...";

        string connectionString = "SERVER=" + server + ";" +
                                  "DATABASE=" + database + ";" +
                                  "UID=" + uid + ";" +
                                  "PASSWORD=" + password + ";";

        connection = new MySqlConnection(connectionString);
    }

    private bool OpenConnection()
    {
        try
        {
            connection.Open();
            return true;
        }
        catch (MySqlException ex)
        {
            switch (ex.Number)
            {
                case 0:
                    MessageBox.Show("Cannot connect to MySQL server.");
                    break;

                case 1045:
                    MessageBox.Show("Invalid username or password.");
                    break;
            }
            return false;
        }
    }

    public string[] GetValidUser(string dbTable, string dbUsername, string dbPassword)
    {
        string query = "SELECT id,email,password FROM " + dbTable +
                       " WHERE email='" + dbUsername +
                       "' AND password='" + dbPassword + "'";

        string[] resultArray = new string[3];

        if (this.OpenConnection() == true)
        {
            MySqlCommand cmd = new MySqlCommand(query, connection);
            MySqlDataReader dataReader = cmd.ExecuteReader();

            while (dataReader.Read())
            {
                resultArray[0] = dataReader.GetInt32(0).ToString();
                resultArray[1] = dataReader.GetString(1);
                resultArray[2] = dataReader.GetString(2);
            }

            dataReader.Close();
            this.CloseConnection();
        }
        return resultArray;
    }
}

数据库class的原始代码可以找到here

这不是 NullReferenceException 的答案 - 我们仍在评论中解决这个问题;这是对安全部分的反馈。

我们首先要看的是SQL注入;这很容易修复 - 见下文(注意我也整理了一些其他东西)

// note: return could be "bool" or some kind of strongly-typed User object
// but I'm not going to change that here
public string[] GetValidUser(string dbUsername, string dbPassword)
{
    // no need for the table to be a parameter; the other two should
    // be treated as SQL parameters
    string query = @"
SELECT id,email,password FROM tbl_user
WHERE email=@email AND password=@password";

    string[] resultArray = new string[3];

    // note: it isn't clear what you expect to happen if the connection
    // doesn't open...
    if (this.OpenConnection())
    {
        try // try+finally ensures that we always close what we open
        {
            using(MySqlCommand cmd = new MySqlCommand(query, connection))
            {
                cmd.Parameters.AddWithValue("email", dbUserName); 
                // I'll talk about this one later...
                cmd.Parameters.AddWithValue("password", dbPassword); 

                using(MySqlDataReader dataReader = cmd.ExecuteReader())
                {
                    if (dataReader.Read()) // no need for "while"
                                           // since only 1 row expected
                    {
                        // it would be nice to replace this with some kind of User
                        //  object with named properties to return, but...
                        resultArray[0] = dataReader.GetInt32(0).ToString();
                        resultArray[1] = dataReader.GetString(1);
                        resultArray[2] = dataReader.GetString(2);

                        if(dataReader.Read())
                        { // that smells of trouble!
                            throw new InvalidOperationException(
                                "Unexpected duplicate user record!");
                        }
                    }
                }
            }
        }
        finally
        {
            this.CloseConnection();
        }
    }
    return resultArray;
}

现在,您可能会想 "that's too much code" - 当然;并且存在可以帮助解决此问题的工具!例如,假设我们做了:

public class User {
    public int Id {get;set;}
    public string Email {get;set;}
    public string Password {get;set;} // I'll talk about this later
}

然后我们可以使用 dapper 和 LINQ 为我们完成所有繁重的工作:

public User GetValidUser(string email, string password) {
    return connection.Query<User>(@"
SELECT id,email,password FROM tbl_user
WHERE email=@email AND password=@password",
      new {email, password} // the parameters - names are implicit
    ).SingleOrDefault();
}

这会您拥有的一切(包括安全地打开和关闭连接),但它干净利落且安全地完成。如果它为 User 方法 returns 一个 null 值,则表示未找到匹配项。如果返回一个非空的 User 实例——它应该包含所有使用基于名称的约定的预期值(意思是:属性 名称和列名称匹配)。

您可能会注意到唯一剩下的代码是实际有用的代码 - 它不是无聊的管道。像 dapper 这样的工具 是你的朋友;使用它们。


终于;密码。您永远不应该存储密码。曾经。从来没有。甚至没有加密。绝不。您应该 存储散列 密码。这意味着您永远无法检索它们。相反,您应该散列用户提供的内容并将其与预先存在的散列值进行比较;如果哈希匹配:那就是通过。这是一个复杂的领域,需要进行重大更改,但您应该这样做。这个很重要。你现在拥有的是不安全的。

除其他外,听起来您的连接字符串有问题 - 来自评论:

While "connection = new MySqlConnection(); connection.ConnectionString = connectionString;" throws an exception the statement "connection = new MySqlConnection();" does not...

这里的区别很简单:在后者中你没有设置连接字符串 - 所以听起来你的连接字符串没有正确转义值(很可能,密码);你可以试试:

var cs = new DbConnectionStringBuilder();
cs["SERVER"] = server;
cs["DATABASE"] = database;
cs["UID"] = uid;
cs["PASSWORD"] = password;
var connectionString = cs.ConnectionString;