我是学生。别担心,我缺乏技能目前不会对任何专业组织造成损害。
我现在正试图建立一个基于数据库的密码系统。还有很多元素要做,但是现在我只想调用一些数据并将其插入到我的项目中。
目前我在if(dr.Read())
获取NullReferenceException相关代码将是:
string strConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=access.mdb";
SqlConnection conn;
SqlCommand cmd;
SqlDataReader dr;
DataTable dt;
和
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
e.SuppressKeyPress = true;
try
{
conn = new SqlConnection(strConnectionString);
cmd = new SqlCommand("select PASSWORD from regulate where NAME = @ID", conn);
cmd.Parameters.AddWithValue("@ID", txtName.Text);
conn.Open();
dr = cmd.ExecuteReader();
}
catch
{ }
if (dr.Read())
{
txtVaries.Text = dr["PASSWORD"].ToString();
}
conn.Close();
if (txtVaries.Text == txtPW.Text)
{
tabControl1.TabPages.Remove(tabPW);
tabControl1.TabPages.Remove(tabMain);
tabControl1.TabPages.Insert(0, tabEdit);
}
答案 0 :(得分:6)
问题不在于您告诉我们的问题,而是之前的问题是由于您尝试使用SqlClient
类连接到MS-Access
数据库。对于Access,您需要使用OleDb
而不是SqlClient。空的try catch块隐藏了该错误。永远不要使用空的挡块,除非真正微不足道的情况可以在不影响程序工作的情况下恢复。
string strConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=access.mdb";
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
e.SuppressKeyPress = true;
using(OLeDbConnection = conn = new OleDbConnection(strConnectionString))
using(OleDbCommand cmd = new SqlCommand("select [PASSWORD] from regulate where NAME = @ID", conn);
{
cmd.Parameters.AddWithValue("@ID", txtName.Text);
conn.Open();
using(OleDbDataReader dr = cmd.ExecuteReader())
{
......... read your data .....
}
}
}
请注意,我已将PASSWORD一词放在方括号内,PASSWORD是Access中的保留关键字,当您在sql命令文本中使用它时需要消除歧义
要看的另一件事是using statement。通过这种方式,您的连接,命令和阅读器将被释放,并且它们使用的资源将在使用块的右括号中释放。这也关闭了连接和读者也可以在例外的情况下简单地使用你的代码。
如下面Mr. Damith中的评论中所述,可以使用ExecuteScalar
而非ExecuteReader
执行仅从一行返回一列的查询。 ExecuteScalar对于性能更好,因为它不构造OleDbDataReader对象,使对象保持忙碌状态,直到关闭为止。它只返回请求的单个值但是你需要谨慎,因为如果查询没有找到任何匹配where子句的用户,则返回值将为null
....
cmd.Parameters.AddWithValue("@ID", txtName.Text);
conn.Open();
object result = cmd.ExecuteScalar();
if(result != null)
txtVaries.Text = result.ToString();
else
MessageBox.Show("No user found!");
答案 1 :(得分:0)
@Steve在SqlClient中使用OleDb连接字符串来回答您的错误的可能原因。我想指出一个整体方法。无论你使用哪个ADO提供程序,你的一个问题是你的空catch块是一个例外的pac-man,它会在没有报告或重新抛出的情况下吃掉任何错误。
然后,可能没有满足dr.Read()的前提条件,但即使连接抛出异常,您仍然会调用dr.Read()。你可以在那一行找到一个空读者。
让我们假设您的用户输入明文密码,然后您应该对其进行哈希处理,并与数据库中的单向哈希进行比较。
相反,代码的一般结构应该是:
try {
using(var conn = new OleDbConnection(...)) {
using(var command = new OleDbCommand("SELECT id, password FROM users WHERE username = ...", conn)) {
conn.Open();
using(var reader = command.ExecuteReader(...)) {
if(reader.Read()) {
var user = new User {
Id = reader.GetInt32(0), PasswordHash = reader.GetString(1)
};
if(User.SHA2Hash(loginPass) == user.PasswordHash)
return user;
else
return null; // or throw security exception
}
}
}
}
}
catch(Exception ex) {
Logger.Error(ex); // REPORT ERROR! DONT EAT IT
throw new SecurityException("Login exception", ex);
}
重要的想法是使用块来为IDisposable资源在失败或成功时正确处理。
请注意,如果您编写查询以使其返回单个值,则可以简化,但我从不对登录代码执行操作,通常我会使用reader返回包含各种字段的用户记录。如果你知道你的查询将返回1行/ col,那么ExecuteScalar()会更好,因为ExecuteReader()会分配一个不必要的IDisposable资源。
答案 2 :(得分:0)
史蒂夫的建议引导我找到以下解决方案:
if (e.KeyCode == Keys.Enter)
{
e.Handled = true;
e.SuppressKeyPress = true;
string strConnection = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=access.mdb";
string strCommand = "SELECT PASSWORD FROM regulate where NAME=@ID";
OleDbConnection conn = new OleDbConnection(strConnection);
OleDbCommand com = new OleDbCommand(strCommand, conn);
OleDbDataReader dr;
com.Parameters.AddWithValue("@ID", txtName.Text);
conn.Open();
dr = com.ExecuteReader();
if (dr.Read())
{
txtVaries.Text = dr["PASSWORD"].ToString();
}
conn.Close();
if (txtVaries.Text == txtPW.Text)
{
tabControl1.TabPages.Remove(tabPW);
tabControl1.TabPages.Remove(tabMain);
tabControl1.TabPages.Insert(0, tabEdit);
}
}