An open datareader associated with this command error in C# - c#

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.

Related

C# INSERT command doesn't work unless database table is empty

I am having an issue with a school assignment.
For some reason, my sql INSERT command doesn't wotk unless the database table is empty.
If it is empty, it works just fine, but if I use if while there are already entries there, it moves to my "catch" block.
My initial connection and reading from the database to load the items was successful.
The following code also succeeds IF I remove the first "for" block (i.e. if I start my connection by deleting all the data from the table and insert the new data after that. Please read the long comment as to why I did two identical "for" blocks to begin with).
If I delete the firs "for" block and also remove the "DELETE" command, it still doesn't work (i.e. the problem isn't the two identical "for" blocks, but something else which prevents the INSERT command from working unless the table is empty).
Using MessageBox.Show I have tested how far the "try" block goes before going to the "catch" block. I have inserted //--// into that place in the following code. As you can see, the last successful line is before the INSERT command.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Favorite fav;
SqlCommand delfav = new SqlCommand("DELETE FROM tblFavorites", conn);
SqlCommand addfav = new SqlCommand("INSERT INTO tblFavorites (Title, url) VALUES (#title, #url)", conn);
addfav.Parameters.Add("#title", SqlDbType.NVarChar, 100);
addfav.Parameters.Add("#url", SqlDbType.NVarChar, 300);
try
{
conn.Open();
for (int i = 0; i < favorites.Items.Count; i++)
{
fav = (Favorite)favorites.Items[i];
addfav.Parameters["#title"].Value = fav.getsettitle;
addfav.Parameters["#url"].Value = fav.getseturl;
//--//
addfav.ExecuteNonQuery();
}
/*
At first I didn't have the above section. The result was that each time the browser closed,
it first deleted the database table and then if there was a problem uploading the new data,
the old list was already gone.
To correct for this, I first attempted to add the favorites to the list (despite the fact
that for a short time it will have double the amount of information). If this process is
unsuccessful, it will skip to the catch block and the original data will be saved.
Only if the INSERT command is successful, I delete the data, then repeat the INSERT command,
already knowing that it is successful.
*/
delfav.ExecuteNonQuery();
for (int i = 0; i < favorites.Items.Count; i++)
{
fav = (Favorite)favorites.Items[i];
addfav.Parameters["#title"].Value = fav.getsettitle;
addfav.Parameters["#url"].Value = fav.getseturl;
addfav.ExecuteNonQuery();
}
}
catch
{
DialogResult dialogResult = MessageBox.Show("There was a problem loading your favorites to the database. Do you still wish to quit? \n (All changes to the favorites list will be lost)", "", MessageBoxButtons.YesNo);
if (dialogResult != DialogResult.Yes)
{
e.Cancel = true;
}
}
finally
{
conn.Close();
}
}
Thanx a lot for your help guys!

C# - New Values not reflecting on Access Database using UPDATE SQL cmd

I'm having problems with updating a row in the Users table of my Access DB. Here is the code below:
private void SaveProfileInfo()
{
try
{
ChangeForeColorOfStatusMsg(Color.Black);
ChangeTextOfStatusMsg("Saving new profile information...");
const string cmd = #"UPDATE Users SET LastName=#LastName,FirstName=#FirstName,MiddleName=#MiddleName,Add_Num=#Add_Num,Add_Street=#Add_Street,Add_Brgy=#Add_Brgy,Add_City=#Add_City,MobileNumber=#MobileNumber,Gender=#Gender WHERE ID=#ID;";
var dbConn = new OleDbConnection(cs);
var dbCmd = new OleDbCommand(cmd, dbConn);
dbCmd.Parameters.AddWithValue("#ID", UserLoggedIn.ID);
dbCmd.Parameters.AddWithValue("#LastName", txtLastName.Text);
dbCmd.Parameters.AddWithValue("#FirstName", txtFirstName.Text);
dbCmd.Parameters.AddWithValue("#MiddleName", txtMiddleName.Text);
dbCmd.Parameters.AddWithValue("#Add_Num", txtUnitNum.Text);
dbCmd.Parameters.AddWithValue("#Add_Street", txtStreet.Text);
dbCmd.Parameters.AddWithValue("#Add_Brgy", GetBrgySelectedItem());
dbCmd.Parameters.AddWithValue("#Add_City", GetCitySelectedItem());
dbCmd.Parameters.AddWithValue("#MobileNumber", txtMobileNumber.Text);
dbCmd.Parameters.AddWithValue("#Gender", GetGenderSelectedItem());
dbConn.Open();
dbCmd.ExecuteNonQuery();
dbConn.Close();
ChangeForeColorOfStatusMsg(Color.MediumSeaGreen);
ChangeTextOfStatusMsg("All changes have been saved! This window will close itself after two seconds.");
Thread.Sleep(2000);
CloseForm();
}
catch (Exception)
{
ChangeForeColorOfStatusMsg(Color.Crimson);
ChangeTextOfStatusMsg("Something went wrong while we were connecting to our database. Please try again later.");
hasFinishedEditting = false;
}
}
This method will be done on a separate thread, when the user updates his profile information.
UserLoggedIn is actually a field of a User class (a class that defines a row in my table), which stores all the info of the user who's currently logged in.
When I run this, it does not produce any exceptions or errors. But when I check my table, the values are not updated.
I copy-pasted these codes from the registration form (which works) that I made with this system, and modified it into an UPDATE cmd than an INSERT cmd.
I also made Change Username and Password Forms that use the same cmd as shown below:
public void ChangePass()
{
try
{
ChangeForeColorOfMsg(Color.Silver);
ChangeTextOfMsg("Changing password...");
const string cmd = "update Users set Pass=#Pass where ID=#ID";
var dbConn = new OleDbConnection(cs);
var dbCmd = new OleDbCommand(cmd, dbConn);
dbCmd.Parameters.AddWithValue("#Pass", txtNewPass.Text);
dbCmd.Parameters.AddWithValue("#ID", UserLoggedIn.ID);
dbConn.Open();
dbCmd.ExecuteNonQuery();
dbConn.Close();
ChangeTextOfMsg("Password successfully changed!");
}
catch (Exception)
{
ChangeForeColorOfMsg(Color.Silver);
ChangeTextOfMsg("A problem occurred. Please try again later.");
}
}
And these codes work for me. So I'm really confused right now as to why this update cmd for the profile information isn't working... Is there something I'm not seeing here?
OleDb cannot recognize parameters by their name. It follows a strictly positional order when sending them to your database for updates. In your code above the first parameter is the #ID but this parameter is used last in your query. Thus everything is messed up.
You just need to move the add of the #ID parameter as last in the collection
As a side note, you should be very careful with AddWithValue. It is an handy shortcut, but it has a dark side that could result in wrong queries.
Take a look at
Can we stop using AddWithValue already?

Having trouble with a SQL Server CE insertion function

I've written the below function, which errors out correctly with non-int input and with int input returns that the audit was started properly. Unfortunately when I check the table I see that the data was never actually inserted.
Any suggestions for what I'm doing wrong?
public string SqlLocation = "Data Source="+ new FileInfo(Path.GetDirectoryName(Application.ExecutablePath) + "\\DRAssistant.sdf");
public string StartAudit(string sqlLocation, string dps)
{
int dpsInteger;
if (!int.TryParse(dps, out dpsInteger))
return "DPS is not a number!";
try
{
var myConnection = new SqlCeConnection(sqlLocation);
myConnection.Open();
var myCommand = myConnection.CreateCommand();
myCommand.CommandText = string.Format("SELECT dps FROM DispatchReviews
WHERE dps = {0}", dpsInteger);
SqlCeDataReader reader = myCommand.ExecuteReader();
if (reader.Read())
{ return "DPS review has already started!"; }
myCommand.CommandText = "INSERT INTO DispatchReviews (dps, starttime,
reviewer) VALUES (#dps, #starttime, #reviewer)";
myCommand.Parameters.AddWithValue("#dps", dpsInteger);
myCommand.Parameters.AddWithValue("#starttime", DateTime.Now);
myCommand.Parameters.AddWithValue("#reviewer", Environment.UserName);
myCommand.Prepare();
myCommand.ExecuteNonQuery();
myCommand.Dispose();
myConnection.Close();
return "Dispatch Review Started!";
}
catch(Exception ex)
{ return "Unable to save DPS!" + ex.Message; }
}
Edit: Turns out this was just an idiot problem--which anybody looking at the SqlLocation could probably figure out--in that every time I built the application a new copy of the .sdf was copied into the application directory, overwriting the previous one. Also, the database I was checking for updates was not the one in the execution directory, but the one that was being copied into it, which is why it was always empty. I noticed this because when I tried to add the same DPS multiple times the first time I would get the DPS review started message, but subsequent attempts would give the error that it had previously been created.
Can you please show us your connection string??
Most likely, if you test this inside Visual Studio, the database file is being copied around (from your initial directory to the output directory where the app runs) and your INSERT will probably work just fine - but you're just looking at the wrong file when you check that fact.

C#, SQL Server 2005 and missing session

I have a problem with a customer. I have this code:
var conn = new SqlConnection(Util.GetConnectionString());
var DataCommand = new SqlCommand();
var sql = "";
// subseccion
try
{
sql = "TRUNCATE TABLE preview_" + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.Connection.Open();
int numcol = DataCommand.ExecuteNonQuery();
sql = "insert into preview_" + tablename+ " select * from " + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
var latest_error = ex.Message;
Util.Add_Event_Log(latest_error);
}
finally
{
DataCommand.Dispose();
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Dispose();
}
This do the next thing, I give a name of a table, it TRUNCATE a table then copy the information from "table" to "preview_table" and it works as expected.
However, we found that if we don't give TRUNCATE permission for the table, it fail. But, my problem is that it does not only fail but also deleting the current session (and may be also restart the server process).
My bet it is a server problem (server 2003) may be it is not patched or anything because I am working inside a try-catch part so it should not fail in this fashion.
My customers says the problem is in the code.
But I am not sure, maybe I should not a sql command in a chain.
Is this happening in the development environment as well as production environment? If so, you need to step through your code with the VS debugger and pin point the line at which the session is being deleted.
You should also check the event logs on the production server to see if they can provide any information.
As stated in the comments by msergey, it may be the Util.Add_Event_Log throwing an exception but you should test this by stepping through the code.
If it is Util.Add_Event_Log causing the issue, move this code out of the catch into its own try/catch statement by declaring an exception variable in the outer scope.
If it does wind up that the use of TRUNCATE is the culprit you might try swapping that out in favor of using a DELETE statement instead. Performance won't be as great, but you wouldn't require elevated user permissions in SQL Server either.

SQL Reading from a DB problem using a DataReader

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.

Categories