SqlDataReader 问题

SqlDataReader issue

例外情况是:

Invalid attempt when no data is present.

但我不知道为什么?有没有人? ps: c=0 最初

private void button2_Click(object sender, EventArgs e)
{
    using (SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename=E:\C #\InsertDeleteUpdate-Login\InsertDeleteUpdate-Login\Database1.mdf;Integrated Security=True"))
    using (SqlCommand command = new SqlCommand("select * from info", connection))
    {
        connection.Open();

        using (SqlDataReader reader = command.ExecuteReader())
        {
            if(reader.HasRows)
            {
                if (reader["Id"].ToString() == textBox1.Text && reader["Password"].ToString() == textBox2.Text)
                {
                    reader.Read();
                    MessageBox.Show("Hello!");
                    c=1;
                }
            }

            if (c==0)
               MessageBox.Show("wrong id or password");
        }
    }
}

关于您的代码,首先要说明的是,您永远不应在数据库中以明文形式存储密码。正确的方法是使用某种密码学(本网站有很多关于存储加密密码的例子)

现在,您的问题是由 SqlDataReader 最初未定位在第一条记录上这一事实引起的。在尝试获取数据之前,您需要请求它读取记录。 (并且,此时调用 HasRows 是无用的,因为如果没有要读取的行,Read 方法将 return 为 false)

        ....
        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if(reader.Read())
            {
                if (reader["Id"].ToString() == textBox1.Text && 
                    reader["Password"].ToString() == textBox2.Text)
                {
                    reader.Read();
                    MessageBox.Show("Hello!");
                    c=1;
                }
            }
            ....

表示请查看Scott Chamberlain的回答以获得更好的解决问题的方法

DataReader.Read returns 一个布尔值,表示是否有更多的数据块要读取,所以如果你有超过 1 个结果,你可以这样做:

while (dr.Read()) {
  // read data for each record here
}

您必须调用 reader.Read() 才能访问它的任何值成员。如果读取成功与否,该函数将 return 一个 bool,最简单的解决方案是用读取调用替换您的 reader.HasRow 调用。

    using (SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename=E:\C #\InsertDeleteUpdate-Login\InsertDeleteUpdate-Login\Database1.mdf;Integrated Security=True"))
    using (SqlCommand command = new SqlCommand("select * from info", connection))
    {
        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if(reader.Read())
            {
                if (reader["Id"].ToString() == textBox1.Text && reader["Password"].ToString() == textBox2.Text)
                {
                    MessageBox.Show("Hello!");
                    c=1;
                }
            }
            if(c==0)
                MessageBox.Show("wrong id or password");
        }
    }

但是,您还可以做很多其他事情来改进您的代码,例如,现在您查询 info 中的每一行,但您只检查第一行 returned。通过使用参数,您可以将检查直接放在查询本身中。

    using (SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename=E:\C #\InsertDeleteUpdate-Login\InsertDeleteUpdate-Login\Database1.mdf;Integrated Security=True"))
    using (SqlCommand command = new SqlCommand("select 1 from info where Id = @Id and Password = @Password", connection))
    {
        command.Parameters.Add("@Id", SqlDbType.NVarChar).Value = textBox1.Text;
        command.Parameters.Add("@Password", SqlDbType.NVarChar).Value = textBox2.Text;
        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if(reader.Read())
            {
                MessageBox.Show("Hello!");
            }
            else
            {
                MessageBox.Show("wrong id or password");
            }
        }
    }

您还可以做很多其他的改进,例如,您不应将密码以明文形式存储在数据库中,而应使用密码哈希函数对其进行哈希处理,但我会将其留给您查看你自己的。

更新:正如史蒂夫在评论中指出的那样,更好的选择是删除 reader 并改用 ExecuteScalar()ExecuteScalar() returns 第一行第一列的值或 null 如果没有 returned 的行(如果数据库中的值为 NULL 它 returns DbNull.Value)。您需要做的就是检查 returned 值是否不等于 null.

    using (SqlConnection connection = new SqlConnection(@"Data Source=(LocalDB)\v11.0;AttachDbFilename=E:\C #\InsertDeleteUpdate-Login\InsertDeleteUpdate-Login\Database1.mdf;Integrated Security=True"))
    using (SqlCommand command = new SqlCommand("select 1 from info where Id = @Id and Password = @Password", connection))
    {
        command.Parameters.Add("@Id", SqlDbType.NVarChar).Value = textBox1.Text;
        command.Parameters.Add("@Password", SqlDbType.NVarChar).Value = textBox2.Text;
        connection.Open();
        var result = command.ExecuteScalar();
        if(result != null)
        {
            MessageBox.Show("Hello!");
        }
        else
        {
            MessageBox.Show("wrong id or password");
        }
    }

SqlDataReader 是一个低级别 API,它很容易出错并使您的代码冗长且难以阅读。

如果可以的话,我建议你使用更高级别的东西。例如 Dapper (https://github.com/StackExchange/dapper-dot-net),它更易于使用、更易于理解并且几乎同样高效。

您的代码类似于

User result = conn.Query<User>(@"
              SELECT * 
              FROM User
              WHERE Id = @Id
              AND Password = @Password", 
              new {  Id = id, Password = password }).FirstOrDefault();

if(User != null) {
    MessageBox.Show("Hello!");
}
else {
    MessageBox.Show("wrong id or password");
}