Invalid attempt to access a field before calling Read() - c#

I am developing a windows app using mysql and c#. In my app there's a signin page having a sign in button. When i press sign in button it do not loads the values from database and gives me the following error:
Invalid attempt to access a field before calling Read().
Visual Studio indicates the my following code as error:
connection.Open();
string query2 = "SELECT * FROM newregistration where ID='" + id+"'";
MySql.Data.MySqlClient.MySqlCommand myCommand2 = new MySql.Data.MySqlClient.MySqlCommand(query2, connection);
// First Name Reader
MySqlDataReader fnamereader = myCommand2.ExecuteReader();
fnamereader.Read();
Fname = fnamereader.GetString(fnamereader.GetOrdinal("FirstName"));
fnamereader.Close();
// Second Name Reader
MySqlDataReader snamereader = myCommand2.ExecuteReader();
snamereader.Read();
Sname = snamereader.GetString(snamereader.GetOrdinal("SecondName"));
snamereader.Close();
// EMAIL ID Reader
MySqlDataReader emailreader = myCommand2.ExecuteReader();
emailreader.Read();
EmailID = emailreader.GetString(emailreader.GetOrdinal("EmailID"));
emailreader.Close();
DataSet ds = new DataSet();
MyAdapter.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
profile windwo = new profile(this.id, this.Fname,this.Sname,this.EmailID);
AddUserProfileInformation win = new AddUserProfileInformation(this.id);
this.Hide();
windwo.Show();
}
else
{
MessageBox.Show("Sorry Wrong information entered.");
}
connection.Close();
Please help me in sorting out the problem as I am new to development.

You don't need to use a seperate MySqlDataReader every column of your sql query. Just use one MySqlDataReader and read all column values in it.
Looks like, you just need to use something like;
if(fnamereader.Read())
{
Fname = fnamereader.GetString(fnamereader.GetOrdinal("FirstName"));
Sname = fnamereader.GetString(fnamereader.GetOrdinal("SecondName"));
EmailID = fnamereader.GetString(fnamereader.GetOrdinal("EmailID"));
}
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
string query2 = "SELECT * FROM newregistration where ID=#id";
MySqlCommand myCommand2 = new MySqlCommand(query2, connection);
myCommand2.Parameters.AddWithValue("#id", id);

You don't need to keep executing the datareader before each field
MySqlDataReader reader = myCommand2.ExecuteReader();
reader.Read();
Fname = reader.GetString(reader.GetOrdinal("FirstName"));
Sname = reader.GetString(reader.GetOrdinal("SecondName"));
EmailID = reader.GetString(reader.GetOrdinal("EmailID"));
reader.Close();
A couple of other things
Please use parameterised sql for binding variables - don't just concatenate the variable into sql text - this has security and performance benefits.
You should check the boolean result of the reader.Read() before using it

Related

C# checking if order number already exists

I've been looking into How to check user id already exists to see how to do this.
I am trying to get this working in my code, however it's not working. I don't get errors or something, but it just write data in database even if order number already exists.
The function:
private void createorderButton_Click(object sender, EventArgs e)
{
SqlConnection myConnection = dbHelper.initiallizeDB();
String query = "INSERT INTO testtabel (knaam, korder) VALUES ('" + knaamTextBox.Text + "','" + kordernrTextBox.Text + "')";
SqlCommand sqlCommand = new SqlCommand(query, myConnection);
SqlCommand cmd = new SqlCommand("select * from testtabel where korder = #korder", myConnection);
SqlParameter param = new SqlParameter();
param.ParameterName = "#korder";
param.Value = kordernrTextBox.Text;
cmd.Parameters.Add(param);
//sqlCommand.Connection.Open();
SqlDataReader reader = sqlCommand.ExecuteReader();
if (reader.HasRows)
{
MessageBox.Show("Order already exist");
}
else
{
reader.Close();
}
// opens execute non query
int rows_inserted = sqlCommand.ExecuteNonQuery();
if (rows_inserted > 0)
{
label2.Text = "Order has been created";
}
else
{
Console.Write("Oops! Something wrong!");
}
}
Sorry for this kinda well known and duplicated question, but for some reason I can't get it working.
You called the wrong command, change
SqlDataReader reader = sqlCommand.ExecuteReader();
to
SqlDataReader reader = cmd.ExecuteReader();
The problem is here:
SqlDataReader reader = sqlCommand.ExecuteReader();
You should execute the other command first
SqlCommand cmd = new SqlCommand("select * from testtabel where korder = #korder", myConnection);
The latter command, when will be executed will tell you if there is any record in the testtabel table. If there is, then you should show the message:
Order already exist
Otherwise, you will execute your first command, that will insert the rows.
By the way, please try to avoid string concatenation, when you write sql queries. It is one of the most well known security holes. You code is open to SQL injections. You could use parameterized queries:
String query = "INSERT INTO testtabel (knaam, korder) VALUES (#knaam, #korder)";
SqlCommand sqlCommand = new SqlCommand(query, myConnection);
sqlCommand.Parameters.Add(new SqlParamete("#knaam",knaamTextBox.Text));
sqlCommand.Parameters.Add(new SqlParamete("#korder",kordernrTextBox.Text));
While your code is full of problems (magic pushbutton, SQL injections, absence of usings), there is main one. The approach you want to implement will fail on concurrent inserts, and must not be used.
Imagine, that two users run this code against the same database, using the same korder value:
1st executes SELECT - record with the given value doesn't exist;
2nd executes SELECT - record with the given value doesn't exist;
1st executes INSERT - record with the given value does exist;
2nd executes INSERT - ooops... we have a duplicate;
To avoid duplicates you must use unique indexes in database. Do not rely on your code.
You check HasRows for INSERT INTO testtabel bla...bla..bla.. not for `elect * from testtabel where korder'
Maybe you can use this (it comes from my head and not compiled, please adjust it with your own case)
private void createorderButton_Click(object sender, EventArgs e)
{
SqlConnection myConnection = dbHelper.initiallizeDB();
String query = "INSERT INTO testtabel (knaam, korder) VALUES ('" + knaamTextBox.Text + "','" + kordernrTextBox.Text + "')";
SqlCommand sqlCommand = new SqlCommand(query, myConnection);
SqlCommand cmd = new SqlCommand("select * from testtabel where korder = #korder", myConnection);
SqlParameter param = new SqlParameter();
param.ParameterName = "#korder";
param.Value = kordernrTextBox.Text;
//sqlCommand.Connection.Open();
SqlDataReader cmdReader = sqlCommand.ExecuteReader();
if (cmdReader.HasRows)
{
MessageBox.Show("Order already exist");
}
else
{
cmdReader.Close();
}
SqlDataReader reader = sqlCommand.ExecuteReader();
// opens execute non query
int rows_inserted = sqlCommand.ExecuteNonQuery();
if (rows_inserted > 0)
{
label2.Text = "Order has been created";
}
else
{
Console.Write("Oops! Something wrong!");
}
}

i cant add data from sql to list of string

In this program I try to get data from SQL into a list of strings and show them in a messageBox. The program should start searching when I type one character in textBox and use this in the query as below:
string sql = " SELECT * FROM general WHERE element='" + textBox1.Text + "' OR element='" + textBox2.Text + "' OR element='" + textBox3.Text + "' OR element='" + textBox4.Text + "'";
MySqlConnection con = new MySqlConnection("host=localhost;user=mate;password=1234;database=element_database");
MySqlCommand cmd = new MySqlCommand(sql, con);
con.Open();
MySqlDataReader reader = cmd.ExecuteReader();
string rd;
rd = reader.ToString();
int i=0;
List<string> item = new List<string>();
while (reader.Read())
{
item.Add(rd["element"].ToString());//i got error in this line
}
for (i = 0; i < item.Count;i++ )
{
MessageBox.Show(item[i]);
}
What am I doing wrong?
What are you doing wrong? a bunch of things:
In your question you write you gen an error but don't tell us what it is.
Exceptions has messages for a reason: so that you will be able to know what went wrong.
As to your code:
You are concatenating values into your select statement instead of using parameterized queries. This creates an opening for sql injection attacks.
You are using an SqlConnection outside of a using statement.
You should always use the using statement when dealing with IDisposable objects.
You assume that rd["element"] always have a value.
If it returns as null from the database, you will get a null reference exception when using .ToString() on it. The proper way is to put it's value into a local variable and check if this variable is not null before using the .ToString() method.
You are using rd instead of reader in your code. the rd variable is meaningless, as it only contain the string representation of MySqlDataReader object.
You have declared rd as string. You probably meant to use the reader object in this loop instead:
while (reader.Read())
{
item.Add(reader["element"].ToString());// change "rd" to "reader"
}
I took the liberty of modifing the SQL to use an IN instead of multiple or statements as well as including the use of parameter into the query and not a string based approach. This should solve your problems.
string elem1 = "#elem1";
string elem2 = "#elem2";
string elem3 = "#elem3";
string elem4 = "#elem4";
List<string> parameters = new List<string>{ elem1, elem2, elem3, elem4 };
string sql = string.Format(" SELECT * FROM general WHERE element IN ({0})", string.Join(',', parameters.ToArray()));
using(MySqlConnection con = new MySqlConnection("host=localhost;user=mate;password=1234;database=element_database"))
{
con.Open();
MySqlCommand cmd = new MySqlCommand(sql, con);
cmd.Parameters.AddWithValue(elem1, textBox1.Text);
cmd.Parameters.AddWithValue(elem2, textBox2.Text);
cmd.Parameters.AddWithValue(elem3, textBox3.Text);
cmd.Parameters.AddWithValue(elem4, textBox4.Text);
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
string message = reader["element"] as string;
if(!string.IsNullOrEmpty(message))
{
MessageBox.Show(message);
}
}
}

Delete a row from a MySQL database only if certain conditions are met

I'm currently working on a simple windows form bug reporter for university and am having trouble. I'm trying to create a query where the user can only delete the bug if:
The bug name exists
The user logged in matches the user that reported the bug.
Currently no matter which use is logged in, the query always returns 'Incorrect User Logged In!' and doesn't delete the bug.
I am a novice at both C# and MySQL, so I'm sure my code isn't the best way of writing it. I apologize if it is hard to read.
EDIT
Here is my current code based on the below answer which still doesnt work. I currently get could not find specified colum in results: bug_submitted_by
connection = new MySqlConnection(connectionString);
string check = "SELECT COUNT(*) FROM bugs WHERE bug_name ='" + this.txt_bug_name.Text + "'AND bug_submitted_by='" + this.lbl_user.Text + "';";
MySqlCommand cmd = new MySqlCommand(check, connection);
MySqlDataReader reader;
connection.Open();
reader = cmd.ExecuteReader();
if (reader.Read())
{
if (reader.GetString("bug_submitted_by").Equals(this.lbl_user.Text))
{
reader.Close();
cmd.Dispose();
string query = "DELETE from bugs WHERE bug_name='" + this.txt_bug_name.Text + "';";
MySqlCommand cmd2 = new MySqlCommand(query, connection);
MySqlDataReader reader2;
reader2 = cmd2.ExecuteReader();
lbl_message.Text = "Bug Deleted!";
reader2.Close();
cmd2.Dispose();
connection.Close();
load_table();
Combo_selection();
reset(this);
}
else
{
lbl_message.Text = "Incorrect user!";
reader.Close();
cmd.Dispose();
connection.Close();
cb_names.SelectedIndex = -1;
}
}
else
{
lbl_message.Text = "Bug Doesn't Exist!";
}
As a side note first, I recommend you look into parameterized queries in C#. I will use that sort of syntax in my answer, but I believe it will be easy enough for you to understand. Given that you are trying to do multiple things (check if bug exists, check if bug was written by that user, delete that bug if condition is met) I recommend you break it up into pieces and put them all back together.
To start, we can just write a query to see if a bug exists. We can be smart and select both the bug name, and who it was submitted by:
using(var conn = new MySqlConnection(ConnectionString)
{
conn.Open();
using(var cmd = new MySqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "SELECT bug_name, bug_submitted_by FROM bugs WHERE bug_name = #name";
cmd.Parameters.AddWithValue("#name", this.txt_bug_name.Text);
using(var reader = cmd.ExecuteReader())
{
if(reader.Read())
{
// Will fill in next
}
}
}
}
Side note: If you're unfamiliar with using, see this question for some insight. This is so you don't have to worry about disposing the objects, they will be disposed once you leave scope of the using block.
So, the above query pulls the row from the table where the bug name matches the input. The inner if statement is used to make sure a row was returned. If it was, check the username and react accordingly. If it doesn't, react accordingly like this:
using(var conn = new MySqlConnection(ConnectionString)
{
conn.Open();
using(var cmd = new MySqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = "SELECT bug_name, bug_submitted_by FROM bugs WHERE bug_name = #name";
cmd.Parameters.AddWithValue("#name", this.txt_bug_name.Text);
using(var reader = cmd.ExecuteReader())
{
if(reader.Read())
{
// We got a row
if(reader.GetString("bug_submitted_by").Equals(this.lbl_user.Text))
{
// If the username matches delete it
using(var cmd2 = new MySqlCommand())
{
cmd2.Connection = conn;
cmd2.CommandText = "DELETE FROM bugs WHERE bug_name = #name";
cmd2.Parameters.AddWithValue("#name", this.txt_bug_name.Text);
cmd2.ExecuteNonQuery();
lbl_message.Text = "Bug Deleted!";
}
}
else
{
// Username doesn't match
lbl_message.Text = "Incorrect user!";
}
}
else
{
// We didn't get a row
lbl_message = "Bug Doesn't Exist!";
}
}
}
}
What this query does not do is protect against the situation where a specific bug name appears twice. If your database restricts that, then you're good. If it doesn't, you'll need to implement some check to make sure you only got one row back.
Please don't hit database with subsequent requests. Not only this unnecessary but may also be misleading because a lot may happen between two calls that you make in a multiuser environment. Another session may already updated or deletes this row.
You can do it all in one go
DELETE
FROM bugs
WHERE bug_name = 'bug1'
AND bug_submitted_by = 'John';
Here is a SQLFiddle demo

How to use input from a textbox and use it in a mysql query in C#

Code:
string connString = "SERVER=;UID=;PASSWORD=;DATABASE=;";
MySqlConnection connect = new MySqlConnection(connString);
MySqlCommand myCommand = connect.CreateCommand();
string input = textBox4.Text;
myCommand.CommandText = "SELECT * FROM project WHERE Id = #input";
connect.Open();
MySqlDataReader reader = myCommand.ExecuteReader();
if (reader.Read())
textBox1.Text = reader["Name"].ToString();
connect.Close();
I am trying to use data from a form textbox (textBox4) and pass that input into a mysql query.
The program works by the user inputting their id number and the form outputs their name into another textbox (textBox1).
When I use either the string 'input' or textBox4.Text itself I get an error message which is: "Fatal error encountered during command execution."
However if I manually type in a correct id number in the code itself it returns the correct vaule
New to C# and mysql sorry for any big errors.
Here is what I would do in your case:
string connString = "SERVER=;UID=;PASSWORD=;DATABASE=;";
MySqlConnection connect = new MySqlConnection(connString);
MySqlCommand myCommand = connect.CreateCommand();
string input = textBox4.Text;
myCommand.CommandText = "SELECT * FROM project WHERE Id = #input";
myCommand.Parameters.Add("#input", SqlDbType.Int).Value = Convert.ToInt32(textBox4.Text);
connect.Open();
MySqlDataReader reader = myCommand.ExecuteReader();
if (reader.Read())
textBox1.Text = reader["Name"].ToString();
connect.Close();
As you can see in the example above I am doing two things:
Sanitizing the input by using a SqlParameter and,
Converting the value of "#input" to a number; which is the assumption based on your question.
Best of luck to you, and please continue learning!
You're almost there, actually. You're using a parameter (#input) and not concatenating strings ("select .. " + text + " from ..."), which is wrong for many reasons.
Just tell your command how to replace that parameter. I guess it's something like this (not sure for MySQL):
myCommand.Parameters.Add("#input", SqlDbType.Int).Value = Convert.ToInt32(textBox4.Text);
Note that I'm assuming that Id is of type SqlDbType.Int. You may change it accordingly.
You must do this before calling myCommand.ExecuteReader(). Most likely, you'll put this line after myCommand.CommandText = "...";.

How to use executeReader() method to retrieve the value of just one cell

I need to execute the following command and pass the result to a label. I don't know how can i do it using Reader. Someone can give me a hand?
String sql = "SELECT * FROM learer WHERE learer.id = " + index;
SqlCommand cmd = new SqlCommand(sql,conn);
learerLabel.Text = (String) cmd.ExecuteReader();
As you can see i create the SQL statement and i execute it, but it does not work. Why?
The console says:
Cannot implicitly SqlDataReader to
String...
How can i get the desired results as String so the label can display it properly.
using (var conn = new SqlConnection(SomeConnectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT * FROM learer WHERE id = #id";
cmd.Parameters.AddWithValue("#id", index);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
learerLabel.Text = reader.GetString(reader.GetOrdinal("somecolumn"))
}
}
}
It is not recommended to use DataReader and Command.ExecuteReader to get just one value from the database. Instead, you should use Command.ExecuteScalar as following:
String sql = "SELECT ColumnNumber FROM learer WHERE learer.id = " + index;
SqlCommand cmd = new SqlCommand(sql,conn);
learerLabel.Text = (String) cmd.ExecuteScalar();
Here is more information about Connecting to database and managing data.
ExecuteScalar() is what you need here
Duplicate question which basically says use ExecuteScalar() instead.

Categories

Resources