two queries in same method - c#

I am trying to execute two queries in the same method but it gives me this exception. I can get red of this exception by declaring new command but is there any way to use the same command?
string id="hi";
connection.Open();
OleDbCommand command1 = new OleDbCommand();
command1.Connection = connection;
string query1 = "select * from products where category='" + comboBox1.Text + "' and subcategory = '" + comboBox2.Text + "' and sizes='" + comboBox3.Text + "'";
command1.CommandText = query1;
OleDbDataReader reader = command1.ExecuteReader();
while (reader.Read())
{
id = reader[0].ToString();
}
textBox1.Text = id;
string query = "insert into category_in (category_id,amount,amount_in) values ('"+ id+"' ,500,300)";
command1.CommandText = query;
command1.ExecuteNonQuery();
MessageBox.Show("saved");
connection.Close();

You need to close your OleDbDataReader object when you are finished with it:
// previous code omitted for brevity
//
while (reader.Read())
{
id = reader[0].ToString();
}
// need to close reader
//
reader.Close();
// run your other query (omitted for brevity)
And as the comment on your question states, the way you construct your query is extremely vulnerable to a SQL inject attack. Parameterize your query the proper way.
As per Dmitry's comment, I completely agree. Wrap your OleDbDataReader object in a using block to call OleDbDataReader.Dispose() upon completion of the block:
using (OleDbDataReader reader = command1.ExecuteReader())
{
// ...
}

You need to close the reader after first execution,so code will be like this
string id="hi";
connection.Open();
OleDbCommand command1 = new OleDbCommand();
command1.Connection = connection;
string query1 = "select * from products where category='" + comboBox1.Text + "' and subcategory = '" + comboBox2.Text + "' and sizes='" + comboBox3.Text + "'";
command1.CommandText = query1;
OleDbDataReader reader = command1.ExecuteReader();
while (reader.Read())
{
id = reader[0].ToString();
}
textBox1.Text = id;
////////code for closing the reader/////////////
reader.Close();
///////////////
string query = "insert into category_in (category_id,amount,amount_in) values ('"+ id+"' ,500,300)";
command1.CommandText = query;
command1.ExecuteNonQuery();
MessageBox.Show("saved");
connection.Close();

Related

Populating textbox from after selecting value from comboBox

I am trying to populate my textbox after selecting a value from my comboBox.
My code is running fine, I don't have any errors when I run it but when I select a value from my comboBox, it's not populating my textbox. See my code below.
private OleDbConnection connection = new OleDbConnection();
public Form1()
{
InitializeComponent();
connection.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\ASUS\Documents\appointment2.accdb";
}
private void Lastname_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
string query = "select * from appointments where patientNo = '" + Lastname.Text + "' ";
command.CommandText = query;
Firstname.Text = reader["firstName"].ToString();
patientNum.Text = reader["patientNo"].ToString();
contactNum.Text = reader["contactNo"].ToString();
}
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error" + ex);
}
}
Two immediate issues that I see:
You are populating the CommandText property of the OleDbCommand object after issuing the ExecuteReader method, meaning there is no SQL statement being evaluated.
The SQL statement should be populated before the ExecuteReader method is issued, i.e.:
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "select * from appointments where patientNo = '" + Lastname.Text + "' ";
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Firstname.Text = reader["firstName"].ToString();
patientNum.Text = reader["patientNo"].ToString();
contactNum.Text = reader["contactNo"].ToString();
}
connection.Close();
The where clause of your SQL statement assumes that patientNo contains string data, which could be incorrect given the name of this field.
Just found out the issue. Had the wrong value to compare my Lastname.Text with and fixed the code's arrangement. Thanks for all your help everyone.
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "select * from appointments where lastName = '" + Lastname.Text + "' ";
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Firstname.Text = reader["firstName"].ToString();
patientNum.Text = reader["patientNo"].ToString();
contactNum.Text = reader["contactNo"].ToString();
}
connection.Close();

Check SQL for Book_Availability before issuing one (BookAvailability-1)

If I put "if, foreach, and else statement under comment //", the program works and Reduces book count by 1 from SQL database. But I want to check IF there is at least 1 available book to give. This code keeps showing me the message in "else" statement if I leave it like this. Help is needed fast, it's my final project, that is needed to be done before 23.07. :(
int book_qty = 0;
SqlCommand cmd2 = connection.CreateCommand();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "SELECT * FROM Book_list WHERE BookName = '" + TextBoxBookName + "'";
cmd2.ExecuteNonQuery();
DataTable dt2 = new DataTable();
SqlDataAdapter da2 = new SqlDataAdapter(cmd2);
da2.Fill(dt2);
foreach (DataRow dr2 in dt2.Rows)
{
book_qty = Convert.ToInt32(dr2["book_qty"].ToString());
}
if (book_qty > 0)
{
SqlCommand cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO Issue_book VALUES(" + TextBoxSearchMembers.Text + ",'" + TextBoxMemberName.Text + "','" + TextBoxMemberContact.Text + "','" + TextBoxMemberEmail.Text + "','" + TextBoxBookName.Text + "', '" + DateTimePicker1.Text + "')";
cmd.ExecuteNonQuery();
SqlCommand cmd1 = connection.CreateCommand();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "UPDATE Book_list SET BookAvailability = BookAvailability-1 WHERE BookName ='" + TextBoxBookName.Text + "'";
cmd1.ExecuteNonQuery();
MessageBox.Show("successful issue");
this.Close();
else
{
MessageBox.Show("Book not available");
}
You are only checking book_qty from the last row in your result set instead of BookAvailability for all rows. You probably want to do something like:
SqlCommand cmd2 = connection.CreateCommand();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "SELECT BookAvailability FROM Book_list WHERE BookName = '" + TextBoxBookName + "'";
var result = cmd2.ExecuteScalar();
book_qty = Convert.ToInt32(result);
You need to make sure that there is only one book with the given bookname available.
In that case just correcting this one line in your code would help as well:
book_qty = Convert.ToInt32(dr2["book_qty"].ToString());
to
book_qty = Convert.ToInt32(dr2["BookAvailability"].ToString());
Otherwise you'd need to query SUM(BookAvailability), but the following code would decrease the amount of books for multiple books at once, that wouldn't be good.
Untested code. I don't have your database. Comments and explanation in line.
private void OPCode()
{
try
{
//keep your connections close to the vest (local)
using (SqlConnection connection = new SqlConnection())
//a using block ensures that your objects are closed and disposed
//even if there is an error
{
using (SqlCommand cmd2 = new SqlCommand("SELECT BookAvailability FROM Book_list WHERE BookName = #BookName", connection))
{
//Always use parameters to protect from sql injection
//Also it is easier than fooling with the single quotes etc.
//If you are referring to a TextBox you need to provide what property is
//being accessed. I am not in a WPF right now and not sure if .Text
//is correct; may be .Content
//You need to check your database for correct data type and field size
cmd2.Parameters.Add("#BookName", SqlDbType.VarChar, 100).Value = TextBoxBookName.Text;
//A select statement is not a non-query
//You don't appear to be using the data table or data adapter
//so dump them extra objects just slow things dowm
connection.Open();
//Comment out the next 2 lines and replaced with
//Edit Update
//var returnVal = cmd2.ExecuteScalar() ?? 0;
//if ((int)returnVal > 0)
//*************************************************************
//Edit Update
//*************************************************************
//in case the query returns a null, normally an integer cannot
//hold the value of null so we use nullable types
// the (int?) casts the result of the query to Nullable of int
Nullable<int> returnVal = (int?)cmd2.ExecuteScalar();
//now we can use the .GetValueOrDefault to return the value
//if it is not null of the default value of the int (Which is 0)
int bookCount = returnVal.GetValueOrDefault();
//at this point bookCount should be a real int - no cast necessary
if (bookCount > 0)
//**************************************************************
//End Edit Update
//**************************************************************
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO issue_book VALUES(#SearchMembers etc", connection))
{
//set up the parameters for this command just like the sample above
cmd.Parameters.Add("#SearchMembers", SqlDbType.VarChar, 100).Value = TextBoxSearchMembers.Text;
cmd.ExecuteNonQuery();
}
using (SqlCommand cmd1 = new SqlCommand("UPDATE Book_list SET BookAvailability = BookAvailability-1 WHERE BookName = #BoxBookName;", connection))
{
cmd1.Parameters.Add("#BoxBookName", SqlDbType.VarChar, 100);
cmd1.ExecuteNonQuery();
}
MessageBox.Show("success");
this.Close();
}
else
{
MessageBox.Show("Book not available");
}
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
}

On adding data, it show "Too few parameters. Expected 1"

The problem says:
Too few parameters. Expected 1.
Here's my database table:
CustomerOrder [CustomerOrder(OrderId, ProdName, ProdPrice, OrderQty, CatName, OrderDate]
Code:
con.Open();
OleDbCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO CustomerOrder(OrderId, ProdName, ProdPrice, OrderQty, CatName, OrderDate)values('" + txtOrderCode.Text + "','" + txtProdName.Text + "', '" + txtProdPrice.Text + "', '" + txtOrderQty.Text + "', '" + txtCatName.Text + "', '" + txtOrderDate.Text + "')";
cmd.ExecuteNonQuery();
tabControl1.SelectedTab = tabPage1;
DataTable dt = new DataTable();
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
dataGridView1.DataSource = dt;
con.Close();
int ordercode, orderqty;
double price;
string prodname, catname;
ordercode = Convert.ToInt32(txtOrderCode.Text);
orderqty = Convert.ToInt32(txtOrderQty.Text);
price = Convert.ToDouble(txtProdPrice.Text);
prodname = Convert.ToString(txtProdName.Text);
I would recommend you use parametrised queries instead as it will prevent you from Sql Injection Attacks. Here is a small example of how it could work with your code
String SqlCommand = "insert into CUSTOMERORDER values (#OrderId, #ProdName,#ProdPrice,#OrderQty, #CatName,#OrderDate)";
SqlCommand cmd = new SqlCommand(SqlCommand , //ConnectionString);
cmd.CommandType = CommandType.Text;
conn.Open();
cmd.Parameters.AddWithValue("#OrderId", txtOrderCode.Text);
cmd.Parameters.AddWithValue("#ProdName", txtProdName.Text );
cmd.Parameters.AddWithValue("#ProdPrice", txtProdPrice.Text);
cmd.Parameters.AddWithValue("#OrderQty", txtOrderQty.Text);
cmd.Parameters.AddWithValue("#CatName", txtCatName.Text.Text);
cmd.Parameters.AddWithValue("#OrderDate", txtOrderDate.Text);
cmd.ExecuteNonQuery();
conn.Close();
Read up on Sql Injection attacks. Doing it this way is much easier, cleaner and most importantly, safer. Also when looking at your code you are setting the text box values after you have ran the Sql command

Invalid attempt to call read when reader is closed when inserting data

i have a button that when clicked inserts data from textbox and combobox fields into database tables, but every time i insert it gives me "Invalid attempt to call read when reader is closed". How can i get rid of this error. And tips on optimising the code are welcome, because i know im a total noob. thanks
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query1 = "insert into location(Storage, Shelf, columns, rows) values(" + txtWarehouse.Text + ", " + txtShelf.Text + ", " + txtColumn.Text + ", " + txtRow.Text + ")";
SqlCommand sqlCmd = new SqlCommand(Query1, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
sqlCon.Close();
}
catch (Exception er)
{
MessageBox.Show(er.Message);
}
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query3 = "SELECT LOCATION_ID FROM LOCATION WHERE storage='" + txtWarehouse.Text + "' AND shelf='" + txtShelf.Text + "' AND columns='"
+ txtColumn.Text + "' AND rows='" + txtRow.Text + "'";
SqlCommand sqlCmd1 = new SqlCommand(Query3, sqlCon);
SqlDataReader dr = sqlCmd1.ExecuteReader(); ;
while (dr.Read())
{
string LocationId = dr[0].ToString();
dr.Close();
string Query2 = "insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID) values ('" + txtSku.Text + "','" + txtNimetus.Text + "', '"
+ txtMin.Text + "', '" + txtMax.Text + "', '" + txtQuan.Text + "', '" + LocationId + "', '" + (cbCat.SelectedIndex+1) + "', '" + (cbMail.SelectedIndex+1) + "')";
SqlCommand sqlCmd = new SqlCommand(Query2, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
}
sqlCon.Close();
}
catch (Exception ed)
{
MessageBox.Show(ed.Message);
}
}
Let's try to make some adjustments to your code.
First thing to consider is to use a parameterized query and not a
string concatenation when you build an sql command. This is mandatory
to avoid parsing errors and Sql Injections
Second, you should encapsulate the disposable objects in a using statement
to be sure they receive the proper disposal when you have finished to
use them.
Third, you can get the LOCATION_ID from your table without running a
separate query simply adding SELECT SCOPE_IDENTITY() as second batch to your first command. (This works only if you have declared the LOCATION_ID field in the first table as an IDENTITY column)
Fourth, you put everything in a transaction to avoid problems in case
some of the code fails unexpectedly
So:
SqlTransaction tr = null;
try
{
string cmdText = #"insert into location(Storage, Shelf, columns, rows)
values(#storage,#shelf,#columns,#rows);
select scope_identity()";
using(SqlConnection sqlCon = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand(cmdText, sqlCon))
{
sqlCon.Open();
using( tr = sqlCon.BeginTransaction())
{
// Prepare all the parameters required by the command
cmd.Parameters.Add("#storage", SqlDbType.Int).Value = Convert.ToInt32(txtWarehouse.Text);
cmd.Parameters.Add("#shelf", SqlDbType.Int).Value = Convert.ToInt32(txtShelf.Text);
cmd.Parameters.Add("#columns", SqlDbType.Int).Value = Convert.ToInt32(txtColumn.Text );
cmd.Parameters.Add("#rows", SqlDbType.Int).Value = Convert.ToInt32(txtRow.Text);
// Execute the command and get back the result of SCOPE_IDENTITY
int newLocation = Convert.ToInt32(cmd.ExecuteScalar());
// Set the second command text
cmdText = #"insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID)
values (#sku, #nimetus,#min,#max,#qty,#locid,#catid,#ordid)";
// Build a new command with the second text
using(SqlCommand cmd1 = new SqlCommand(cmdText, sqlCon))
{
// Inform the new command we are inside a transaction
cmd1.Transaction = tr;
// Add all the required parameters for the second command
cmd1.Parameters.Add("#sku", SqlDbType.NVarChar).Value = txtSku.Text;
cmd1.Parameters.Add("#nimetus",SqlDbType.NVarChar).Value = txtNimetus.Text;
cmd1.Parameters.Add("#locid", SqlDbType.Int).Value = newLocation;
.... and so on for the other parameters required
cmd1.ExecuteNonQuery();
// If we reach this point the everything is allright and
// we can commit the two inserts together
tr.Commit();
}
}
}
}
catch (Exception er)
{
// In case of exceptions do not insert anything...
if(tr != null)
tr.Rollback();
MessageBox.Show(er.Message);
}
Notice that in the first command I use parameters of type SqlDbType.Int because you haven't used single quotes around your text. This should be verified against the real data type of your table columns and adjusted to match the type. This is true as well for the second command where you put everything as text albeit some of those fields seems to be integer (_location_id_ is probably an integer). Please verify against your table.

DataReader exception in C#

Let me first explain my code.
fetch fare for selected destination(reader1)
fetch * for selected user id(reader2)
insert data to process new balance(cmd3)
retrieve new balance as calculated field (reader3)
update travel account (cmd5)
clear calc_tb for next customer input(cmd6)
My problem is while running, I get this error:
There is already an open DataReader associated with this Command which must be closed first.
How can I handle more than one DataReader accessing different tables?
Is DataReader similar to resultset in Java ?
I'm a beginner in coding.
string id,
destin, num;
id = txt_id.Text;
destin = cb_destin.Text;
num = cb_num.Text;
string sql1 = "SELECT fare FROM route_info WHERE route_name='" +destin + "' ";
string sql2 = "SELECT * FROM trav_acc WHERE user_id='" + id + "'";
con.Open();
cmd1 = new SqlCommand(sql1, con);
reader1 = cmd1.ExecuteReader();
while(reader1.Read())
{
string fare = (string)reader1["fare"];
cmd2 = new SqlCommand(sql2, con);
reader2 = cmd2.ExecuteReader();
string cur_bal = (string)reader2["cur_bal"];
reader2.Close();
string calc1 = "INSERT INTO calc_tb VALUES('" + cur_bal + "','" + num + "','" + fare + "')";
string calc2 = "SELECT cur_bal - (nof_tickets * fare) AS new_bal FROM calc_tb";
cmd3 = new SqlCommand(calc1, con);
cmd3.ExecuteNonQuery();
cmd4 = new SqlCommand(calc2, con);
reader3 = cmd4.ExecuteReader();
while(reader3.Read())
{
string new_bal = (string)reader3["new_bal"];
string update = "UPDATE trav_acc SET cur_bal='" + new_bal + "',last_bal='" + cur_bal + "' WHERE user_id='" + id + "' ";
cmd5 = new SqlCommand(update, con);
cmd5.ExecuteNonQuery();
string clear = "DELETE FROM calc_tb";
cmd6 = new SqlCommand(clear, con);
cmd6.ExecuteNonQuery();
}
}
con.Close();
MessageBox.Show("Thank you for using EasyTravel.Come again soon!");
}
It seems that reader3 and cmd4 are defined outside of the code you are showing us, but at least they are defined outside the loop for reader1. So if your reader1 contains more than one row, reader3 and cmd4 will be assigned again, but the "old" reader3 is never closed. Close reader3 when it's finished reading. Or use a using statement, which will take care of the closing automatically.
using (DataReader reader1 = cmd1.ExecuteReader()) {
....
while (reader1.Read()) {
....
using (DataReader reader3 = cmd4.ExecuteReader()) {
while (reader3.Read()) {
}
} //reader3 is closed here automatically
}
} //reader1 is closed here automatically
Furthermore, I'm not sure if I remember correctly, but I think it's not possible to have two open readers on the same connection. I may be wrong with this, though.

Categories