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;
我的 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;