I've got an application that, when a button is clicked, reads an access database for products and lists them in a listbox and dataGridView. The connection command text is northwind_command.CommandText = "SELECT ProductName, UnitPrice FROM Products WHERE UnitPrice > (#price_greater_than)";
On the first click the program will work, but when the button is clicked a second time an exception is thrown. As this thrown exception causes the data reader to "crash", the third click will work as if it was the first. The fourth click will throw the same exception. If I had to guess, I'd say that the the datareader isn't closing properly, but it should be. Here is the code for that part of the program:
northwind_connection.ConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source='U:\Programming\C#\Week 13\Exercises\ExerciseA1\bin\northwind.mdb';Persist Security Info=True";
northwind_command.Connection = northwind_connection; // connects the command to the connection.
northwind_command.CommandText = "SELECT ProductName, UnitPrice FROM Products WHERE UnitPrice > (#price_greater_than)"; // sets the query used by this command.
northwind_command.Parameters.AddWithValue("#price_greater_than", price_greater_than);
try
{
northwind_connection.Open(); // opens the connection.
northwind_reader = northwind_command.ExecuteReader(); // reads the data from the connection while executing the command.
dataGridView1.Columns.Add("ProductName", "Product Name");
dataGridView1.Columns.Add("UnitPrice", "Product Price");
while (northwind_reader.Read())
{
dataGridView1.Rows.Add(northwind_reader["ProductName"], northwind_reader["UnitPrice"]);
listBox1.Items.Add(northwind_reader["ProductName"] + "\t" + northwind_reader["UnitPrice"]);
}
}catch(Exception mistake)
{
MessageBox.Show(mistake.ToString());
}
northwind_connection.Close();
EDIT: I've solved the issue with some help, but would like to figure out why it was happening in the first place. The offending line was northwind_command.Parameters.AddWithValue("#price_greater_than", price_greater_than);. The line above that one was modified to: northwind_command.CommandText = "SELECT ProductName, UnitPrice FROM Products WHERE UnitPrice > " + price_greater_than; and the program now works correctly.
That method was causing the exception to be thrown, which can be seen below:
I checked the exception message and line 50 contains this code: northwind_reader = northwind_command.ExecuteReader();, which confirms that the AddwithValue method was causing the error.
My bet is that something is not getting disposed of properly. Try modifying your code to use using statements:
using(var northwindConnection = new OleDbConnection())
{
//Set your connection info
using(var northwindCommand = northwindConnection.CreateCommand())
{
//Set your command info
try
{
// Open your connection and any other things
// needed before executing your reader
using(var reader = northwindCommand.ExecuteReader()){
//Do what you need with your reader
}
}
catch(Exception mistake)
{
MessageBox.Show(mistake.ToString());
}
}
}
When a class implements IDisposable, you really should wrap that in a using statement. Doing so will make sure that all resources are properly disposed. In the case of database connections, this will make sure that your connection is closed, so no need to call myConn.Close().
Another things that might be causing issues is that you are adding columns to dataGridView1 every time the button is clicked.
Edit:
Since you found the problem is with AddWithValue, let me add this:
In past experience, I have had issues using the #paramName syntax with OleDbCommand. Try using ?paramNam syntax instead. I have also had issues if the name is to long, so try shortening it.
You should use Paramaters.Add(string, OleDbType).Value = value instead of Paramaters.AddWithValue(string, value). The reason being is that AddWithValue has to interpret the type of the column, and it can sometimes get it wrong.
Add northwind_reader.Close() at the end of the while loop.
You need to close reader before use it again.
example:
while (reader.Read())
{
string value = reader.GetValue(0).tostring();
}
reader.Close();
in your case is northwind_command.close()
Related
Hello and thanks for reading. We have a Filemaker database that we would like to read using System.Database.ODBC - We successfully connect and also, if we want to update, delete, or insert, it works fine using command.ExecuteNonQuery(), the problem arises when we try to read data, in other words, when we try to get a recordset... I did notice it reads successfully from the database using command.ExecuteReader(), the problem arises when it tries to loop through the columns and rows, specifically when it tries to get the column name, datatype, etc... in other words, the metadata.
for example, in the following query: "SELECT company FROM AC__Account WHERE ""Account Id"" = 30" it should return a single field, in this case "company", and a single record, the #30.
When I use command.ExecuteReader() and check for reader.FieldCount, it returns correctly 1, if I check for rows using reader.HasRows it returns true, this is how I know, it received the data correctly.
Now, if I try to inspect the reader object by adding a breakpoint, and the ResultView (in this case, an Enumerable), instead of an enumerable I found an error as follow:
and inspecting the full StackTrace this is what I get
at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode) at System.Data.Odbc.OdbcDataReader.GetColAttributeStr(Int32 i, SQL_DESC v3FieldId, SQL_COLUMN v2FieldId, HANDLER handler) at System.Data.Odbc.OdbcDataReader.GetName(Int32 i) at System.Data.Common.DbEnumerator.BuildSchemaInfo() at System.Data.Common.DbEnumerator.MoveNext() at System.Linq.SystemCore_EnumerableDebugView.get_Items()
Is there a way to bypass Microsoft to get the metadata? or am I missing something? Please help, please check my code.
using (OdbcConnection connection = new OdbcConnection(Conf.GetSection("ConnectionStrings:FilemakerPre1").Value))
{
string queryString = #"SELECT company FROM AC__Account WHERE ""Account Id"" = 30";
OdbcCommand command = new OdbcCommand(queryString, connection);
connection.Open();
var reader = command.ExecuteReader();
Console.WriteLine($"Field count: {reader.FieldCount}");
Console.WriteLine($"Any rows?: {reader.HasRows}");
while (reader.Read())
{
Console.WriteLine(reader[0]);
}
}
I am try to display data from a database. However even though data exists in the database no records are being returned.
If run the following query:
select Id, Movie_Name from [MovieTable] where Movie_Name like '10,000 BC'
I get being returned:
However when running a similar query in c# nothing seems to be being returned. My code is as follows:
try
{
string query = "select * from [MovieTable] where Movie_Name like #MovieName";
string movieName = "10,000 BC"
using (SqlConnection sconnection = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=E:\Application\ApplicationDatabase.mdf;Integrated Security=True");)
using (SqlCommand command = new SqlCommand(query, sconnection))
{
sconnection.Open();
command.Parameters.AddWithValue("#MovieName", movieName);
using (SqlDataReader oReader = command.ExecuteReader())
{
if (oReader != null)
{
while (oReader.Read())
{
MessageBox.Show(oReader["Movie_Name"].ToString());
}
}
}
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
The message box never appears. Adding a third message box just above oReader.Read() displays the message "Invalid attempt to read when no data is present". Is there something i am missing?
Your code runs fine for me, with little adaptions though.
You query the value of the attribute 'Year' which is not present in the table and in your first query:
oReader.GetOrdinal("Year")
That causes an exception in the sample.
Single Quotes are not needed since you are using a parametrized query.
For debugging calls to System.Diagnostics.Debug.WriteLine() are more useful than MessageBoxes
Issue was with the data itself. Field was incorrectly set as type "Text". I altered the fields data type to "nvarchar(MAX)". It also mean't the query could be altered to:
"select * from [MovieTable] where Movie_Name = #MovieName"
I want to build a simple loop to check incoming data from SQL server, compare it to a textfield, and execute non query if there are no duplicates.
I wrote this code:
try
{
bool exists = false;
conn = new SqlConnection(DBConnectionString);
SqlCommand check_user = new SqlCommand("SELECT usrEmail FROM tblUsers", conn);
SqlCommand add_user = new SqlCommand("INSERT INTO tblUsers (usrEmail, usrPassword, usrRealname, usrIsowner) VALUES (#email, #pass, #name, #owner)", conn);
// (I have removed all the paramaters from this code as they are working and irrelevant)
conn.Open();
SqlDataReader check = check_user.ExecuteReader();
while (check.Read())
{
if (Convert.ToString(check[0]) == UserEmail.Text)
{
MessageBox.Show("The email you entered already exists in the system.");
exists = true;
break;
}
}
if (exists == false)
{
add_user.ExecuteNonQuery();
}
else
{
return;
}
}
catch (Exception ex)
{
MessageBox.Show("There was a problem uploading data to the database. Please review the seller's details and try again. " + ex.Message);
return;
}
finally
{
conn.Close();
}
I used breakpoints and saw that the code runs the while loop fine, but when it reaches the ExecuteNonQuery command, it returns an error message:
there is already an open datareader associated with this command which
must be closed first
I tried to use a check.Close(); command, but when I do, it suddenly gets stuck with the duplicate email error message for reasons passing understanding.
Additionally, there was a fix I tried in which the data actually WAS sent to the database (I saw it in SQL Server Management Studio), but still gave an error message... That was even stranger, since the nonquery command is the LAST in this function. If it worked, why did it go to the catch?
I have searched the site for answers, but the most common answers are MARS (I have no idea what that is) or a dataset, which I do not want to use in this case.
Is there a simple solution here? Did I miss something in the code?
The simples way out would be:
using(SqlDataReader check = check_user.ExecuteReader())
{
while (check.Read())
{
if (Convert.ToString(check[0]) == UserEmail.Text)
{
MessageBox.Show("The email you entered already exists in the system.");
exists = true;
break;
}
}
}
That said, there are some serious problems with this code.
First of all, you don't really want to read all users just to check that an email address is already taken. select count(*) from tblUsers where usrEmail = #email is fine...
...or not, because there's a possibility of a race condition. What you should do is add a unique constraint on a usrEmail column and just insert into tblUsers, catching violations. Or you can use merge if you feel like it.
Next, you don't really want to have your data access code all over the place. Factor it out into separate classes/methods at least.
can't I open new data reader in existing data reader?? plzz help me. i'm new to c#
string statement11 = "SELECT Planning FROM allow where NPLID=(SELECT MAX(NPLID) FROM allow)";
SqlCommand myCommand11 = new SqlCommand(statement11, con1);
SqlDataReader plan2 = myCommand11.ExecuteReader();
while(plan2.Read())
if (!plan2.IsDBNull(0) && "ok" == plan2.GetString(0))
{
string statement99 = "SELECT Dropplan FROM NPLQAnew where NPLID=(SELECT MAX(NPLID) FROM allow)";
SqlDataReader myReader1 = null;
SqlCommand myCommand114 = new SqlCommand(statement99, con1);
SqlDataReader plandrop = myCommand114.ExecuteReader();
while (plandrop.Read())
if (plandrop.IsDBNull(0) && plandrop.GetString(0) == "Red")
{
Lblplan1.BackColor = System.Drawing.Color.Red;
}
else if (plandrop.IsDBNull(0) && "amber" == plandrop.GetString(0))
{
Lblplan1.BackColor = System.Drawing.Color.Orange;
}
else if (plandrop.IsDBNull(0) && "Green" == plandrop.GetString(0))
{
Lblplan1.BackColor = System.Drawing.Color.Green;
}
plandrop.Close();
this.Lblplan1.Visible = true;
}
plan2.Close();
By default, the SQL Server client will not let you open two simultaneous queries on the same connection. If you are in the process of reading the results of one data reader, for example, you cannot use the same connection to start reading from a second. And, with the way that SQL Server connection pooling works, even asking for a "new" connection is not guaranteed to work either.
You have a couple of options on how to fix this. The first is to refactor your code to eliminate the nested SQL execute calls; for example, load the results of your first query into memory before you loop through and process them.
An easier answer is to enable "MARS" - Multiple Active Recordsets - on your connection. This is done be setting the "MARS Connection=True option on the connection string to turn the feature on. This is generally pretty safe to do, and it's only off by default to preserve the pre-2005 behavior for old applications, but you the linked article does give some guidelines.
You can try setting MultipleActiveResultSets=True in your connection string
No you cant perform this on same connection, but you can achieve by
Multiple Active Result Sets (MARS), am hoping you having sqlserver 2005 and above.
or
You need to different connection to be opened for the second command.
Use USING statement. The using statement calls the Dispose method on the object in the correct way, and it also causes the object itself to go out of scope as soon as Dispose is called.
For error : There is already data reader attached to current connection string. Try to close the data reader first.
after this line
if (!plan2.IsDBNull(0) && "ok" == plan2.GetString(0))
{
//open a new sql connection here
//your string statement99
:
:
// then close your second sql connection here before the last }
}
I have a table of Users (tblUsers) which contains details of University staff. I am trying to populate a text box with the names of lecturers associated with a selected module.
I am getting all UserIDs associated with a particular module, testing if the User is a lecturer, if so then I add the ID to an ArrayList.
I then iterate through this array and call the method below during each iteration passing through the current ID.
However, if you look at the method below I am using a SqlDataReader and am getting an error while reading from it on this line:
txtLecturerName.Text += myReader["First_Name"].ToString();
The error message is:
'myReader["First_Name"]' threw an exception of type 'System.IndexOutOfRangeException'
The table layout I am using is below the method code. Any help with this would be greatly appreciated, I am one cup of coffee away from putting my head through the screen.
public void outputLecturerNames(string lecturerID)
{
// Create a new Connection object using the connection string
SqlConnection myConnection = new SqlConnection(conStr);
// If the connection is already open - close it
if (myConnection.State == ConnectionState.Open)
{
myConnection.Close();
}
// 'using' block allows the database connection to be closed
// first and then the exception handling code is triggered.
// This is a better approach than using a 'finally' block which
// would close the connection after the exception has been handled.
using (myConnection)
{
try
{
// Open connection to DB
myConnection.Open();
SqlCommand selectCommand = new SqlCommand(selectQuery, myConnection);
// Declare a new DataReader
SqlDataReader myReader;
selectQuery = "SELECT * FROM tblUsers WHERE User_ID='";
selectQuery += lecturerID + "'";
myReader = selectCommand.ExecuteReader();
while (myReader.Read())
{
txtLecturerName.Text += myReader["First_Name"].ToString();
txtLecturerName.Text += " ";
txtLecturerName.Text += myReader["Last_Name"].ToString();
txtLecturerName.Text += " , ";
}
myReader.Close();
}
catch (Exception err)
{
Console.WriteLine("Error: " + err);
}
}
}
tblUsers:
[User_ID][First_Name][Last_Name][Email_Address]
In your method, the variable selectQuery is not declared, and it is used as parameter to SqlCommand before it is assigned the query string on tblUsers.
You've probably misspelled a column name.
In general, you should never write SELECT * FROM ....
Instead, you should select only the columns you need.
This will make your program run faster by only querying the information that you need, and can produce better error messages.
This error is created when the column name given is not found. If you are anything like me, you've probably checked it several times, but is the table name correct (correct database, correct schema) and is the column name correct?
http://msdn.microsoft.com/en-us/library/f01t4cfy.aspx
You might try fully qualifying the name of the table (database.dbo.tblUsers). This would ensure that you are hitting the table you think you are. Also, try and put the names of the columns into the SQL statement. If they are not correct, your SQL statement will not execute properly.