For loop only writing to my mssql stored procedure once? - c#

I have a for loop that collects the data from my C# web form and writes each item that it finds to a database via a stored procedure.
The problem I'm having is that it's writing to the database only once. I have stepped through the code in visual studio and inserted test variables to check that all the data is there and is being captured, which it is. Also because the stored procedure is executing correctly the first time I know that it's working.
So I think the problem might be with how I've got the try catch in the for loop?
Or possibly something else entirely - I could really do with a fresh pair of eyes and someone to point me in the right direction!
protected void log_hd_number()
{
////write results to DB.
SqlConnection conn = new SqlConnection("Data Source=;Initial Catalog=;Integrated Security=True");
SqlCommand cmd = conn.CreateCommand();
SqlDataReader reader;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "insert_requested_hd";
Dictionary<String, String> hdSize = new Dictionary<String, String>();
hdSize.Add("hardDiskSizeData1", hardDiskSizeData1.Text);
hdSize.Add("hardDiskSizeData2", hardDiskSizeData2.Text);
int numberRequested = 2;
for (int i = 1; i <= numberRequested; i++)
{
cmd.Parameters.AddWithValue("#hd_size", hdSize["hardDiskSizeData" + i]);
cmd.Parameters.AddWithValue("#number_requested", numberRequested);
cmd.Parameters.AddWithValue("#vm_id", 15);
try
{
conn.Open();
reader = cmd.ExecuteReader();
reader.Close();
}
catch (Exception exc)
{
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
EDIT:
SP:
ALTER PROCEDURE [dbo].[insert_requested_hd]
-- Add the parameters for the stored procedure here
#hd_size nvarchar(150),
#number_requested int,
#vm_id int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO dbo.hard_disk_size
(
hd_size,
number_requested,
vm_id
)
VALUES
(
#hd_size,
#number_requested,
#vm_id
)

you keep adding parameters to cmd in the loop without ever clearing the old ones. Maybe that's the issue.
also i'm not sure you can open a conn after it has been closed. i think you have to make a new one.

Calling a stored procedure in a loop is not a good idea, I guess.
If it is MS SQL Server, use user-defined table type and table-valued parameter!
This allows you to call the stored procedure once.
How to Save Object Graph in Master-Detail Relationship with One Stored Procedure.

You should be using
cmd.ExecuteNonQuery();
Instead of
reader = cmd.ExecuteReader();

You are closing your connection after the first loop.
If you really want to go to DB every loop you have to open a new connection for each round.
Either way, I think you shouldn't hit the DB so many times. Have you think about BulkInsert?
Take a look here

Related

IF NOT EXISTS INSERT INTO ELSE UPDATE SQL

What am I doing wrong?
I am working on creating a windows forms C# application that is database centered. In my forms app I have 4 textboxes that inserts its data into one simple Database table.
If i type something into textBox1(Customer_Name), and I click on a button called "Check and Save', I would like the action to check if the Customer_Name exists.
If it does not exist, it should insert the data into the database.
If it does exist, it should update my database with the information entered into textBox1-4
I have this code:
private void Button1_Click(object sender, EventArgs e)
{
con.Open();
SqlCommand cmd = new SqlCommand("IF NOT EXISTS (SELECT * FROM [Customers] WHERE Customer_Name=#aa BEGIN INSERT INTO [Customers](Customer_Name,Cellphone_Number,Telephone_Number,Alternative_Number) VALUES(#aa,#bb,#cc,#dd) END ELSE BEGIN UPDATE [Customers] SET Customer_Name=#aa, Cellphone_Number=#bb, Telephone_Number=#cc, Alternative_Number=#dd END", con);
cmd.Parameters.AddWithValue("#aa", textBox1.Text);
cmd.Parameters.AddWithValue("#bb", textBox2.Text);
cmd.Parameters.AddWithValue("#cc", textBox3.Text);
cmd.Parameters.AddWithValue("#dd", textBox4.Text);
con.Close();
}
No information gets entered into the database on button click not does any information update.
What am I doing wrong?
You are not executing the command.
Your code should have a cmd.ExecuteNonQuery(); once the command is fully configured.
However, once you add that, you will get a syntax error message from SQL Server.
This is because you are missing a closing parenthesis for the Exists operator.
Please note that is far from being the only thing you are doing wrong in this code:
you are mixing UI code with application code.
you are using a global variable (or at the very least a field) to hold your instance of the SqlConnection.
you are using terrible names for your parameters and texboxes.
You are using AddWithValue.
the pattern for "upsert" you are using is cumbersome and potentially problematic.
Here are better alternatives:
First
You should read about the n-tier architectural pattern.
For winforms, it's usually implemented using MVP.
For one thing, instead of sending textboxes data directly into your database, create a Customr class to hold the data, and use that to pass customer data around in your code.
Second
Best practice is to use a local variable inside a using statement to ensure disposale of the SqlConnectioninstance and the return of the underlying connection to the connection pool.
Third
Imagine having to change something in a code that looks like this vs. changing something in a code that looks like that:
cmd.Parameters.Add(#"CustomerName", SqlDbType.NVarChar).Value = customerName;
Now you don't have to read at the SQL to figure out what that parameter means -
the less time and effort you have to spend understanding the code the better.
Fourth
The article in the link explains why it's problematic in details,
but the main point is that the data type of the parameter must be inferred from usage,
and that might yield errors because of data type wrongly inferred - or even worst - wrong data silently entered into the database.
Fifth
A better pattern is to first update, and then insert conditionally - like demonstrated in Aaron Bertrand's answer here - and in a multi-user (or multi-threaded) environment wrap the entire thing in a transaction.
All that being said, a revised code should look more like this:
private void AddOrUpdateCustomer(Customer customer)
{
// Data validity tests omitted for brevity - but you should ensure
// customer has all it's properties set correctly.
// Readable, properly indented code - Isn't that much easier to debug?
var sql = #"
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE [Customers]
SET
Cellphone_Number = #Cell,
Telephone_Number = #Telephone,
Alternative_Number = #Alternative
WHERE Customer_Name = #Name
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO [Customers](Customer_Name, Cellphone_Number, Telephone_Number, Alternative_Number)
VALUES(#Name, #Cell, #Telephone, #Alternative)
END
COMMIT TRANSACTION;";
// connectionString should be obtained from configuration file
using(var con = new SqlConnection(connectionString))
{
using(var cmd = new SqlCommand(sql, con))
{
cmd.Parameters.Add(#"Name", SqlDbType.NVarChar).Value = customer.Name;
cmd.Parameters.Add(#"Cell", SqlDbType.NVarChar).Value = customer.Cellphone;
cmd.Parameters.Add(#"Telephone", SqlDbType.NVarChar).Value = customer.Telephone;
cmd.Parameters.Add(#"Alternative", SqlDbType.NVarChar).Value = customer.AlternativeNumber;
con.Open();
cmd.ExecuteNonQuery();
}
}
}
I checked your code there are few problems.
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
con.Open();
SqlCommand cmd = new SqlCommand("IF NOT EXISTS (SELECT * FROM [Customers] WHERE Customer_Name=#aa BEGIN INSERT INTO [Customers](Customer_Name,Cellphone_Number,Telephone_Number,Alternative_Number) VALUES(#aa,#bb,#cc,#dd) END ELSE BEGIN UPDATE [Customers] SET Customer_Name=#aa, Cellphone_Number=#bb, Telephone_Number=#cc, Alternative_Number=#dd END", con);
cmd.Parameters.AddWithValue("#aa", textBox1.Text);
cmd.Parameters.AddWithValue("#bb", textBox2.Text);
cmd.Parameters.AddWithValue("#cc", textBox3.Text);
cmd.Parameters.AddWithValue("#dd", textBox4.Text);
cmd.ExecuteNonQuery();
con.Close();
}
There is a missing bracket and condition is missing in update statement. I have fixed the query. You can try again and also check the SQL Connection string.
IF NOT EXISTS (SELECT * FROM [Customers] WHERE Customer_Name=#aa) BEGIN INSERT INTO [Customers](Customer_Name,Cellphone_Number,Telephone_Number,Alternative_Number) VALUES(#aa,#bb,#cc,#dd) END ELSE BEGIN UPDATE [Customers] SET Customer_Name=#aa, Cellphone_Number=#bb, Telephone_Number=#cc, Alternative_Number=#dd WHERE Customer_Name=#aa END
Insert Data
Update Data
I have not check the SQL statement.
But can you check the code after adding, because without Executing the command Database you can not see changes.
cmd.ExecuteNonQuery();
before
con.Close();

Call to Stored Procedure not updating as expected

I have a mystery with a stored procedure that I'm calling from code behind(C#). I am baffled because I have added watchpoints my code on the C# side and everything seems to be having the values that they should be going into the call to the stored procedure however, the procedure runs without any errors that I can tell and yet my table doesn't get updated with the values that I feel they should.
The SP gets three values passed to it.
Record ID (#Record_ID), Column to update (#UpdColumn), and the value to place in that column (#UpdValue).
Here is my SP that I am calling:
ALTER PROCEDURE [dbo].[Single_Col_Update]
-- Add the parameters for the stored procedure here
#Record_ID INT,
#UpdColumn CHAR,
#UpdValue NVARCHAR
AS
BEGIN
SET NOCOUNT ON;
IF #UpdColumn = 'TicketNumber'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET TicketNumber = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'TicketClosed'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET TicketClosed = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'Notes'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET Notes = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'Exception_ID'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET ExceptionID = #UpdValue
WHERE RecID = #Record_ID;
END
Here is the code segment calling the SP:
foreach (string record in recordnumber)
{
SqlConnection con = new SqlConnection("Data Source=MyDataSource");
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "Single_Col_Update";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.AddWithValue("#Record_ID", Convert.ToInt32(record));
cmd.Parameters.AddWithValue("#UpdColumn", Session["UpdColumn"]);
cmd.Parameters.AddWithValue("#UpdValue", Session["UpdValue"]);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
Since all the variables are right, I'm not sure why this isn't updating. Hoping some of you may see an error here.
UPDATED 5/19/2017 1:40PM Central -
Steve,
I attempted to implement the call as you prescribed below. I only made to variations to what you provided:
'cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 1024);' // instead of 255 because the column I'm feeding there is an NVarChar(MAX) I will likely have to go back and modify this to be greater than 1024. There didn't appear to be a MAX value that I could put in there so for testing the 1024 will suffice.
omitted the 'transaction.Rollback();' // I kept red lining on the word 'transaction' and despite what I tried I couldn't get it to validate it.
Bottom line is that after implementing the code below the results were exactly the same as before. The code executed without reporting any errors either via the Consol.Write I added or through the VS 2017 IDE.
SqlTransaction transaction;
try
{
using (SqlConnection con = new SqlConnection("Data Source=MyDataSource"))
using (SqlCommand cmd = new SqlCommand("Single_Col_Update", con))
{
con.Open();
transaction = con.BeginTransaction();
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Record_ID", SqlDbType.Int);
cmd.Parameters.Add("#UpdColumn", SqlDbType.NVarChar, 255);
cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 1024);
foreach (string record in recordnumber)
{
cmd.Parameters["#Record_ID"].Value = Convert.ToInt32(record);
cmd.Parameters["#UpdColumn"].Value = Session["UpdColumn"].ToString();
cmd.Parameters["#UpdValue"].Value = Session["UpdValue"].ToString();
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
So I'm still where I was, but I have taken notice of what you shared and I concur with all you stated. I hadn't noticed that I was opening and closing the connection there and was not aware of other things you had shared.
However the quandary remains!
Update 05/22/2017 10:45AM Central time:
I realized that I was trying to stuff NVarchar type into to a Varchar type in my stored procedure. Once corrected the modifications that I made based on Steve's feedback worked just fine. I haven't tried it but I'm assuming that what I had to begin with would have worked if the types had matched to begin with, but Steve's example is cleaner so I am not even going back to test the old way. Thanks again Steve!
The problem is in the declaration of this parameter
#UpdColumn CHAR,
in this way the Stored Procedure expects a SINGLE char, not a string.
Thus all the following if statements are false and nothing will be updated
Change it to
#UpdColumn NVARCHAR(255)
The same is true for the #UpdValue parameter. Again, only a single char is received by the stored procedure. Doesn't matter if you pass a whole string.
If you don't specify the size of the NVARCHAR or CHAR parameters the database engine will use only the first char of the passed value.
I want also to underline the comment above from Alex K. While it should not give you a lot of gain it is preferable to open the connection and create the command with the parameters outside the loop. Inside the loop just change the parameters values and execute the sp
SqlTransaction transaction;
try
{
using(SqlConnection con = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand("Single_Col_Update", con))
{
con.Open();
transaction = con.BeginTransaction())
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Record_ID", SqlDbType.Int);
cmd.Parameters.Add("#UpdColumn", SqlDbType.NVarChar, 255);
cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 255);
foreach (string record in recordnumber)
{
cmd.Parameters["#Record_ID"].Value = Convert.ToInt32(record));
cmd.Parameters["#UpdColumn"].Value = Session["UpdColumn"].ToString();
cmd.Parameters["#UpdValue"].Value = Session["UpdValue"].ToString();
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
catch(Exception ex)
{
// show a message to your users
transaction.Rollback();
}
I have also added all your loop inside a transaction to confirm all the inserts as a whole or reject all in case of errors.
CHAR should only be used when a column is a fixed length. When you use it with varying length strings, the results will be usually not what you expect because the parameter/column will be padded with spaces which is why your IF statements are failing.
Don't use the CHAR type for #UpdColumn. Use NVARCHAR instead for this column and also it's a good practice to specify a length for both this parameter and the UpdValue parameter in your stored procedure and then match this closely when calling the stored procedure from your C# code.

C# Sql Command for Insering into a database

I am new to Visual C# programming language and recently i was trying to make a application that is supposed to insert into a local database of users some data but every times my code runs and the insertion works fine the database does not update.This is the code that i am using
try
{
cn.Open();
SqlCommand insert = new SqlCommand();
insert.CommandText = "insert into Clienti (Nume,Prenume,Parola,Email) values(#Nume,#Prenume,#Parola,#Email)";
insert.Connection = cn;
insert.Parameters.AddWithValue("#Nume", register_nume.Text);
insert.Parameters.AddWithValue("#Prenume", register_prenume.Text);
insert.Parameters.AddWithValue("#Parola", register_password.Text);
insert.Parameters.AddWithValue("#Email", register_email.Text);
insert.ExecuteNonQuery();
SqlDataReader reader = insert.ExecuteReader();
while (reader.Read()) { }
MessageBox.Show("Added succesfully");
}
catch (Exception ex)
{
MessageBox.Show(""+ex);
}
I already tried the property Copy to output and it doesn't seems to work.
I am sorry for any grammar mistakes that i made,I would be grateful for any help.
insert.ExecuteNonQuery();
SqlDataReader reader = insert.ExecuteReader();
while (reader.Read()) { }
You only need the ExecuteNonQuery, it will run the INSERT. You need to use ExecuteReader instead only when you're running a statement that produces result sets (eg. SELECT). So it should be:
insert.ExecuteNonQuery();
Its because you have to now read the database using a select statement, you cant use an INSERT SQL statement to read.
You could add the following immediately after your insert.
using(var selectCmd = new SqlCommand("SELECT Nume,Prenume,Parola,Email FROM Clienti WHERE Nume = #Nume", cn))
{
selectCmd.Parameters.AddWithValue("#Nume", register_nume.Text);
using(SqlDataReader reader = selectCmd.ExecuteReader())
{
while (reader.Read()) { }
}
}
That said if you want to know IF the row was inserted or how many records were inserted ExecuteNonQuery returns the number of rows affected. You could change that part of the code like this:
var recordsAffected = insert.ExecuteNonQuery();
if(recordsAffected > 0)
MessageBox.Show("Added succesfully");
else
MessageBox.Show("Nothing happened");
Although in this particular case it would not make sense because if nothing was inserted it would probably be caused by an Exception.
Some side notes
Always wrap types that implement IDisposable in using blocks (see code above as example). It ensures that resources are always released as soon as you are done with them even if an Exception is thrown.
Never swallow Exceptions! Either recover from one and log it or do not catch it at all. If you swallow it you will never know if/why your code broke.
This part of your code is preparing the SQL command that you are running
cn.Open();
SqlCommand insert = new SqlCommand();
insert.CommandText = "insert into Clienti (Nume,Prenume,Parola,Email) values(#Nume,#Prenume,#Parola,#Email)";
insert.Connection = cn;
insert.Parameters.AddWithValue("#Nume", register_nume.Text);
insert.Parameters.AddWithValue("#Prenume", register_prenume.Text);
insert.Parameters.AddWithValue("#Parola", register_password.Text);
insert.Parameters.AddWithValue("#Email", register_email.Text);
The SQL command that your code is running, is an INSERT statement, which only adds a new record to your table.
This statement runs the command that you setup earlier:
insert.ExecuteNonQuery();
If I understand correctly, here you are trying to read the data from the table again:
SqlDataReader reader = insert.ExecuteReader();
while (reader.Read()) { }
Problem is, your command is NOT set for reading. To read data, you need to use a SELECT statement. Something like this:
insert.CommandText = "SELECT Nume,Prenume,Parola,Email from Clienti" ;
So, to read the data after executing the insert, you should do this:
insert.CommandText = "SELECT Nume,Prenume,Parola,Email from Clienti" ;
SqlDataReader reader = insert.ExecuteReader();
while (reader.Read()) { }
You ar executing 2 times the query:
insert.ExecuteNonQuery();
SqlDataReader reader = insert.ExecuteReader();
Try to get the affected rows
int rows = insert.ExecuteNonQuery();
I would suggest creating a stored procedure in your database and just execute the SP.
It's always better to execute SP from code and leave the SQL programming in the DB.

INSERT INTO c# to Microsoft access

I am trying to insert the text inside some text boxes into a database that I have in access. The code produces no errors but does not seem to add the items to the database.
The Database is called 'Database' the table is called 'TotalPlayerName' and the field is called 'Player Name'.
There are other fields in the table.
for(int i = 0; i < numberOfPlayers; i++){
using (OleDbConnection connection = new OleDbConnection(#"CONNECTION STRING"){
using (OleDbCommand command = new OleDbCommand(#"INSERT INTO TotalPlayerName ([Player Name]) VALUES(#p1)", connection)){
connection.Open();
command.Parameters.Add("#p1", OleDbType.VarWChar).Value = Convert.ToString(textBox[i].Text);
command.ExecuteNonQuery();
}
}
}
You might just need to declare #p1 because you call it in the INSERT statement, but it is never defined as a variable such as: varchar, int, ect, ect. This might work for what you are trying to do:
using (OleDbCommand command = new OleDbCommand(#"DECLARE #p1 VARCHAR(50) INSERT INTO TotalPlayerName ([Player Name]) VALUES(#p1)", connection)){
Also if at all possible i would definitely make it a stored procedure if you can. This works with SQL not sure if it will work with MS Access, but i would imagine so. The other thing you might want to do is make sure that it's finding the correct DB.
Database.dbo.TotalPlayerName
But that is probably not the issue, probably just the lack of variable declaration.
While I don't see what's specifically wrong with your code, I can tell you your methodology is off a bit. Specifically, for every iteration of your loop you are:
Establishing a connection to the database
Creating the insert command, creating a parameter and assigning the value
Executing the insert
It would be better all around if you did steps 1 and part of 2 once and then executed the statement within the loop like this:
using (OleDbConnection conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\foo.accdb"))
{
conn.Open();
OleDbCommand command = new OleDbCommand(
#"INSERT INTO TotalPlayerName ([Player Name]) VALUES (#p1)", conn);
command.Parameters.Add(new OleDbParameter("#p1", OleDbType.VarChar));
for (int i = 0; i < numberOfPlayers; i++)
{
command.Parameters[0].Value = textbox[i].Text;
try
{
command.ExecuteNonQuery();
}
catch (Exception ex)
{
// do something
}
}
conn.Close();
}
I assume textbox is an array or list of actual Text Box controls. If that's the case, then textbox[i].Text is already a string, and you shouldn't need to do anything special to make OLE recognize it as such.
On a final note -- add that try/catch and put a breakpoint there. Are you SURE it's not failing? If you are running in debug mode, there is no guarantee that your program will halt -- it may just return back to the form without reporting any error. It may not be until you attempt to deploy the app that you see the actual error occurring.

One a DELETE query, how to know that there was nothing to delete?

I'm currently polishing a C# app in relation with a SQL-Server base.
It's quite simple, you can add or remove entries from the SQL table via some fields from the application.
Question is :
On a Delete action, I've made this kind of query :
DELETE FROM table
WHERE ID = #ID
It deletes what I ask it to delete, BUT what if the query doesn't find anything in the DB ?
How can I detect that ?
Because in this case, the application deletes nothing, and no exception is raised.
To make it short, I'd just like to tell the user that there's nothing to delete in this case.
If you are using SqlCommand object, there is a method called ExecuteNonQuery. The method return how many rows are affected. So, zero means none.
private static void CreateCommand(string queryString,
string connectionString)
{
using (SqlConnection connection = new SqlConnection(
connectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
command.Connection.Open();
int rowsAffected = command.ExecuteNonQuery(); // <== this
}
}
delete from table where ID=#id
select ##rowcount
This will return how many rows there were actually deleted. You do not need the exists.
have a look at ##ROWCOUNT variable
http://technet.microsoft.com/en-us/library/ms187316.aspx

Categories