Not allow Nested SqlCommand? - c#

I have 2 SqlCommand's, one of them is nested. Why it does not allow me to issue the second SqlCommand (I am using a separate SQLCommand)? It gives an error
There is already an open DataReader associated with this Command which must be closed first.
If I use a separate SqlConnection, it is fine.
SqlCommand cmd = new SqlCommand(qry, cn);
SqlDataReader rd = cmd.ExecuteReader();
while (rd.Read())
{
....
try
{
SqlCommand cmd2 = new SqlCommand(qry2, cn);
cmd2.ExecuteNonQuery();
}
catch (Exception e)
{
// I get this error here
// System.Data; There is already an open DataReader associated with this Command which must be closed first.
}
}

The message is obvious: you can't use same connection for different SqlCommand instance at the same time while DataReader is still open. The SqlDataReader instance explanation already said that:
While the SqlDataReader is being used, the associated SqlConnection is
busy serving the SqlDataReader, and no other operations can be
performed on the SqlConnection other than closing it. This is the case
until the Close method of the SqlDataReader is called. For example,
you cannot retrieve output parameters until after you call Close.
The common solution for this problem is using MultipleActiveResultSets=True on connection string:
<add name="ConnectionName" connectionString="[connection string];MultipleActiveResultSets=True" ... />
Then, use DataTable instead iterating DataReader directly:
var dt = new DataTable();
dt.Load(rd);
foreach (DataRow row in dt.Rows)
{
// other stuff
try
{
SqlCommand cmd2 = new SqlCommand(qry2, cn);
cmd2.ExecuteNonQuery();
}
catch (Exception e)
{
// throw exception
}
}
Additionally, you can put simple check if the previous connection is still open (i.e. serving DataReader) using SqlConnection.State property:
// close if connection still open
if (cn.State == ConnectionState.Open)
{
cn.Close();
}
// open if connection already closed
if (cn.State == ConnectionState.Closed)
{
cn.Open();
}
The simple checks above should be put on any part of code which requesting SqlConnection.

Related

How to insert data into local database using C#

I am working on my first project using local database on C#. I have searched on internet different code for inserting data, but nothing has worked for me. I am trying different code, the problem that occurs to me is the built in functions they are using doesn't show up in my code. Can someone share the authentic code for inserting, retrieving and deleting in local database ?
The recent code that I have tried, some exception is occurring in SqlCeConnection.
This is my code :
string str="Data Source=(localdb)shop_database;Initial Catalog=shop_database;Integrated Security=True";
SqlCeConnection con = new SqlCeConnection(str);
SqlCeDataAdapter sda = new SqlCeDataAdapter();
SqlCeCommand cmd = con.CreateCommand();
cmd.CommandText = "Insert into Account_details (Account_No,Customer_name,Customer_father_name,Profession,Mobile_No,Office_Address,House_Address,CNIC,Item_name,Item_color,Item_model,Item_engine_NO,Item_chasis_NO,Cash_price,Installment_price,Advance_given,Amount_left,Monthly_Installment,Monthly_Rent,Date_of_giving,Sponsor_name,Sponsor_father_name,Sponsor_profession,Sponsor_Address,Sponsor_CNIC,Sponsor_Mobile_No) values (#Account_No,#Customer_name,#Customer_father_name,#Profession,#Mobile_No,#Office_Address,#House_Address,#CNIC,#Item_name,#Item_color,#Item_model,#Item_engine_NO,#Item_chasis_NO,#Cash_price,#Installment_price,#Advance_given,#Amount_left,#Monthly_Installment,#Monthly_Rent,#Date_of_giving,#Sponsor_name,#Sponsor_father_name,#Sponsor_profession,#Sponsor_Address,#Sponsor_CNIC,#Sponsor_Mobile_No)";
cmd.Parameters.AddWithValue("#Account_No", this.Textbox0.Text);
cmd.Parameters.AddWithValue("#Customer_name", this.Textbox1.Text);
cmd.Parameters.AddWithValue("#Customer_father_name", this.Textbox2.Text);
cmd.Parameters.AddWithValue("#Profession", this.Textbox3.Text);
cmd.Parameters.AddWithValue("#Mobile_No", this.Textbox4.Text);
cmd.Parameters.AddWithValue("#Office_Address", this.Textbox5.Text);
cmd.Parameters.AddWithValue("#House_Address", this.Textbox6.Text);
cmd.Parameters.AddWithValue("#CNIC", this.Textbox7.Text);
cmd.Parameters.AddWithValue("#Item_name", this.Textbox14.Text);
cmd.Parameters.AddWithValue("#Item_color", this.Textbox15.Text);
cmd.Parameters.AddWithValue("#Item_model", this.Textbox16.Text);
cmd.Parameters.AddWithValue("#Item_engine_NO", this.Textbox17.Text);
cmd.Parameters.AddWithValue("#Item_chasis_NO", this.Textbox18.Text);
cmd.Parameters.AddWithValue("#Cash_price", this.Textbox19.Text);
cmd.Parameters.AddWithValue("#Installment_price", this.Textbox20.Text);
cmd.Parameters.AddWithValue("#Advance_given", this.Textbox21.Text);
cmd.Parameters.AddWithValue("#Amount_left", this.Textbox25.Text);
cmd.Parameters.AddWithValue("#Monthly_Installment", this.Textbox22.Text);
cmd.Parameters.AddWithValue("#Monthly_Rent", this.Textbox23.Text);
cmd.Parameters.AddWithValue("#Date_of_giving", this.Textbox24.Text);
cmd.Parameters.AddWithValue("#Sponsor_name", this.Textbox8.Text);
cmd.Parameters.AddWithValue("#Sponsor_father_name", this.Textbox9.Text);
cmd.Parameters.AddWithValue("#Sponsor_profession", this.Textbox10.Text);
cmd.Parameters.AddWithValue("#Sponsor_Address", this.Textbox11.Text);
cmd.Parameters.AddWithValue("#Sponsor_CNIC", this.Textbox12.Text);
cmd.Parameters.AddWithValue("#Sponsor_Mobile_No", this.Textbox13.Text);
try
{
cmd.ExecuteNonQuery();
MessageBox.Show("Successfully saved");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
To edit, insert, in general interact with your database you need the class SqlCommand. First you create a connection to your database with an SqlConnection object. Then you pass the SQL statement as a string and the connection into the constructor of the SqlConnection class. Little example:
SqlConnection con = new SqlConnection("server=localhost;database=test_db;uid=root;password=yourpassword");
SqlCommand cmd = new SqlCommand("select * from your_table", con);
To retreive the data from the database you need to use the SQL Statements. For example an SQL statement is something like:
insert into my_table (value1, value2)
values("Example", "Insertion");
When you created your SqlConnection and the SqlCommand you need to open the database connection and execute the command. Wether it's a command for receiving information from the database or editing the database you use ExecuteReader() or ExecuteNonQuery(). For example when you want to receive all the Information stored in one table you use:
SqlConnection con = new SqlConnection("connection string as shown above");
SqlCommand cmd = new SqlCommand("select * from example_table", con);
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while(reader.Read())
Console.WriteLine(reader[<table_index or attribute Name>]);
And finally dont forget to call the close method on your SqlConnection and SqlDataReader object
You are probably making two mistakes:
Problem 1. Your connecting string looks like wrong. Instead of:
Data Source=(localdb)shop_database;Initial Catalog=shop_database;Integrated Security=True";
It should be:
Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=shop_database;Integrated Security=True";
Problem 2. You are not opening the connection before executing the command. Your code in the block should be like this:
try
{
conn.Open(); // Open the connection
cmd.ExecuteNonQuery();
MessageBox.Show("Successfully saved");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close(); // Close the connection
}
As a best practice, I recommend that you use "using" block to create your connection. In that case, you don't have to explicitly close the connection and set it to null:
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
conn.Open();
// Remaining code
}
}
catch(Exception ex)
{
// Manage your exception here
}

c# there is already an open datareader associated with this command which must be closed first

I want to search data from another SQL Server table (Stocks_Item) to this form (IC) dataGridView1, I can view that data (from Stocks_Item) in this dataGridView1 but I couldn't search. Please help me.
Here is my code:
private void button5_Click(object sender, EventArgs e)
{
conn.Close();
try
{
conn.Open();
SqlCommand selectCommand = new SqlCommand("Select * from Stocks_Item where Stock_No = #Stocks_no", conn);
selectCommand.Parameters.Add(new SqlParameter("Stocks_no", txtsearch_stock_no.Text.ToString()));
SqlDataReader reader = selectCommand.ExecuteReader();
bool rowFound = reader.HasRows;
SqlDataAdapter data = new SqlDataAdapter("Select * from Stocks_Item", conn);
DataTable dt = new DataTable();
data.Fill(dt);
dataGridView1.DataSource = dt;
/* SqlDataAdapter sda;
DataTable dt1;
sda = new SqlDataAdapter("select * FROM colombo_branch ",conn);
dt1 = new DataTable();
sda.Fill(dt1);
dataGridView.DataSource = dt1;*/
MessageBox.Show("Search Found", "Form", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
conn.Close();
}
There are many flaws in your code, but the one that is specifically causing your error is because you are not properly closing the SqlDataReader here:
SqlDataReader reader = selectCommand.ExecuteReader();
bool rowFound = reader.HasRows;
As a result, the following line, which also attempts to open a data reader internally throws the exception:
data.Fill(dt);
Normally, you want to use a SqlDataReader inside a using block, something like:
bool rowFound;
using(SqlDataReader reader = selectCommand.ExecuteReader())
{
rowFound = reader.HasRows;
}
... to ensure proper and timely disposal. But in this case, I fail to see the point of that block of code anyways. So why not just remove it altogether?
Additional comments:
Avoid global connections. The fact that the method begins by closing the connection is not a good sign.
Always use using blocks around your SqlConnection, SqlCommand, SqlDataReader, SqlDataAdapter, etc... instances to make sure you don't run into these types of errors or leak resources.
I believe that SqlDataAdapter opens/closes a connection for you. Since you're doing that earlier in your code and using a reader against it, that would be the cause. You could use either the reader or the adapter, but typically wouldn't want to mix them in one connection without properly closing/disposing.
FYI, the MSDN SqlDataAdapter code example utilizes this auto open/close behavior of SqlDataAdapter

Cant read from SQL Server database in C#

I am currently working on a college assignment in which I am having trouble reading data from a SQL Server database. I'm attempting to read the Dentist Name column and then add these names to a combobox.
However when I input the column name it shows an error.
My table is called dentistInfo with columns Dentist ID, Dentist Name, Dentist Surname, DOB and Gender.
Eventually when I get to the reading done correctly I will then hopefully be able to populate their details when the names are selected from the combobox.
public partial class Dentist_Info : Form
{
Surgery mySurgery = new Surgery();
private SqlConnection conn;
private SqlCommand cmd;
private SqlDataAdapter da;
Surgery _formsSurgery;
public Dentist_Info(Surgery SurgeryToDisplay)
{
_formsSurgery = SurgeryToDisplay;
}
public void FillCombo()
{
SqlConnection conn = new SqlConnection(#"Data Source = GGJG; Initial Catalog = DentistDB; Integrated Security = True");
SqlCommand SelectCommand = new SqlCommand("SELECT * FROM DentistInfo", conn);
SqlDataReader myreader;
conn.Open();
try
{
myreader = SelectCommand.ExecuteReader();
while (myreader.Read())
{
string dname = myreader.GetString("Dentist Name");
comboBox1.Items.Add(dname);
}
conn.Close();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
finally
{
if (conn != null)
{
conn.Close();
}
}
}
Pro-tip: If you want to ask about an error, post the error.
In any case, the problem is easy to spot in this case. There's no overload of GetString that accepts a string as an argument - you can only use the column index.
So either you need to pass the column index (myreader.GetOrdinal("Dentist Name")) or you need to use the indexer ((string)myreader["Dentist Name"]). In either case, make sure to handle possible NULL values properly - data reader simply throws an exception if you try to read an SQL NULL value.
As an aside, your try...catch can be simplified (and more useful):
When you want to rethrow an exception, use throw; (no "argument"). Wrap the exception only if you have some information to add.
The catch clause isn't required. It seems that you're only using it for the finally - it's perfectly fine to just use try...finally without the catch.
conn can never be null in the finally clause - your try isn't long enough.
For a pattern like this, you want to use using instead of try...finally anyway. You should also use using for the data reader.
Try this: I recommend you putting [] in the Dentist Name since it has a space between the two words, which might cause you the error, or change the name from the Database to DentistName
public void FillCombo()
{
SqlConnection conn = new SqlConnection(#"Data Source = GGJG; Initial Catalog = DentistDB; Integrated Security = True");
SqlCommand SelectCommand = new SqlCommand("SELECT * FROM DentistInfo", conn);
conn.Open();
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(SelectCommand);
da.fill(ds);
foreach(DataRow dr in ds.Tables[0].Rows)
{
comboBox1.Items.Add(dr["[Dentist Name]"].ToString());
}
conn.Close();
}
As per addition instead of using conn.Open() and conn.Close(), as the answer of the first user you can surround the connection inside a using like so:
using(SqlConnection conn = new SqlConnection(#"Data Source = GGJG; Initial Catalog = DentistDB; Integrated Security = True"))
{
//your codes here no need for conn.Open() and conn.Close()
}

ADO.Net c# 0x80131904 must declare scalar variable/function

I'm trying to use a function via a SQL connection I've done everywhere else in my application (only here it give the error, not the rest of the application). When i searched for what that error code meant the responses i found say it's an error when one can't connect to SQL server? but it doesn't give a solution.
here is my c# code
SqlConnection connection = Database.GetConnection();
DataTable dt = new DataTable("CRC");
SqlCommand cmd = new SqlCommand("SELECT dbo.CalcRentalCharge(#RentalStartDateTime,#RentalEndDateTime,#CarTypeID)", connection);
try
{
connection.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#RentalStartDateTimetext", SqlDbType.DateTime).Value = RentalStartDateTimeBox.Text;
cmd.Parameters.Add("#RentalEndDateTimetext", SqlDbType.DateTime).Value = RentalEndDateTimeBox.Text;
cmd.Parameters.Add("#CarTypeIDtext", SqlDbType.Int).Value = CarTypeID.Text;
connection.Open();
Decimal rentalChange = (Decimal)cmd.ExecuteScalar();
connection.Close();
MessageBox.Show("The rental change is: " + rentalChange.ToString());
if (dr.HasRows)
{
dt.Load(dr);
dataGridView1.DataSource = dt;
}
}
connection.Close();
Can you help me get this FUNCTION to work?
Don't use cmd.ExecuteReader() before adding parameter to command object.
It gives error,
add parameter to command and then cmd.execureReader()
You have a copy/paste error in your variable name:
In the line
cmd.Parameters.Add("#RentalStartDateTimetext", SqlDbType.DateTime).Value = RentalStartDateTimeBox.Text;
the string
RentalStartDateTimetext
needs to be
RentalStartDateTime
In addition to that, because it will pop up as your next error: Your opening and closing of the connection is wrong. Use a using block for the connection as well and open it directly after the start of the block. You don't need to close it manually, the using-block will do that for you.

Reuse of SqlConnection and SqlDataReader

If I want to run multiple SELECT queries on different tables, can I use the same SqlDataReader and SqlConnection for all of them?? Would the following be wise?? (I typed this up fast, so it lacks try/catch):
MySqlCommand myCommand = new MySqlCommand("SELECT * FROM table1", myConnection);
myConnection.Open();
SqlDataReader myDataReader = myCommand.ExecuteReader();
while(myReader.Read())
{
//Perform work.
}
myCommand.commandText = "SELECT * FROM table2";
myReader = myCommand.ExecuteReader();
while(myReader.Read())
{
//Perform more work
}
myReader.Close();
myConnection.Close();
Thanks a lot.
You can use the same connection for each of them, as long as you do not try to execute multiple queries concurrently on the same connection from different threads.
As for the data reader, you are not actually re-using the reader, each call to ExecuteReader returns a new instance of a new reader, all you are re-using is the variable that maintains the reference to the reader. Here in lies a problem, you are only explicitly closing the last reader and leaving the first to be GC'd at some later time.
You can reuse the Command as well, but remember if you supply parameters etc. you will need to clear them for the next query unless they apply to the next query as well.
You should use try/finally blocks to ensure that you clean up the resources, or here is a quick change to your code to use using statements to ensure resource clean-up even if there is an exception that prevents the rest of the code from executing.
using (var myConnection = GetTheConnection())
{
myConnection.Open();
var myCommand = new MySqlCommand("SELECT * FROM table1", myConnection))
using (var myDataReader = myCommand.ExecuteReader())
{
while(myReader.Read())
{
//Perform work.
}
} // Reader will be Disposed/Closed here
myCommand.commandText = "SELECT * FROM table2";
using (var myReader = myCommand.ExecuteReader())
{
while(myReader.Read())
{
//Perform more work
}
} // Reader will be Disposed/Closed here
} // Connection will be Disposed/Closed here
Note: GetTheConnection is just a place holder function for what ever mechanism you are using to get your connection instance.
I generally use Adapters so I am rusty on the details of the reader, but I think you are on the right track.
One item of note in your code is that each call to ExecuteReader should be generating a new data reader. You may be reusing the variable name, but the reference to the existing reader is discarded and replaced by a new one on each call. Point being, close the previous reader before using ExecuteReader to get a new one.

Categories

Resources