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();
Related
I am currently trying to implement SQL into a project with Unity3D. So far, I was able to do "normal" UPDATE, ADD, DELETE, DROP, ALTER, INSERT".
Trying to go a step further, I am trying to insert prepared statements, using this link as a guide
Here is my code :
SqlConnection sqlConnection = new SqlConnection(Connection.connectionString)
sqlConnection.Open();
SqlCommand cmd = new SqlCommand(null, sqlConnection);
cmd.CommandText = "INSERT INTO IngredientTypes (Name) VALUES (#name)";
SqlParameter nameParam = new SqlParameter("#name", SqlDbType.Text, 155);
nameParam.Value = Name;
cmd.Parameters.Add(nameParam);
cmd.Prepare();
cmd.ExecuteNonQuery();
My table looks like so :
CREATE TABLE IngredientTypes
(
IngredientTypeID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(155)
);
I get this error :
SQLException : Incorrect systax near '1'.
System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds. Protocol.TdsInternalErrorMessageEventArgs e)
Help please? Thank you in advance.. I can't find where I did wrong.
You can reduce that code quite a bit with no loss of function, and even some important improvements (for example, this will close the connection even if an exception is thrown):
using (var sqlConnection = new SqlConnection(Connection.connectionString))
using (var cmd = new SqlCommand("INSERT INTO IngredientTypes (Name) VALUES (#name)", sqlConnection))
{
cmd.Parameters.Add("#name", SqlDbType.VarChar, 155).Value = Name;
sqlConnection.Open();
cmd.ExecuteNonQuery();
}
I'm not sure what's causing that exception in your existing code, though, because 1 is not used anywhere in that query. I suspect the problem has something to do with SqlDbType.Text, since that is not the correct type to use with a VarChar column, but it seems just as likely there's code somewhere we haven't seen yet that's changing your SQL command text.
Definitely the Prepare() method in your link is not needed for Sql Server. It's inherited here from DbCommand, where it's included because it's an important part of the API for some other databases, but Sql Server has handled this automatically for more than 10 years now.
SqlDbType.Text Is not the same as varchar. I don’t believe Text types have a length you specify.
Could you try below? Using the "using" structure is safer for sql connections by the way, the connection automatically closes when your process is done.
using (SqlConnection sqlConnection = new SqlConnection(Connection.connectionString))
{
SqlCommand command = new SqlCommand("INSERT INTO IngredientTypes (Name) VALUES (#name)", connection);
command.Parameters.Add("#name", SqlDbType.Varchar, 155);
command.Parameters["#name"].Value = Name; //make sure Name is string.
try
{
sqlConnection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I tried your code exactly as it is and found no issue. Though there are few compilation errors (missing ; in line 1 and Name variable should be coming as parameter) but I am sure you know that. If you have posted your table structure and code exactly the same as you have in your project, then there is no problem in this code.
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
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.
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.
I using this code to entering .
private void button1_Click(object sender, EventArgs e)
{
string Coonstring = "datasource=localhost;port=3306;username=root;password=****;Charset=utf8";
string cmd = "Insert into project.name_registry (name ) values('" + this.txt.Text + "');";
MySqlConnection connectionDatabase = new MySqlConnection(Coonstring);
MySqlCommand cmddata = new MySqlCommand(cmd, connectionDatabase);
MySqlDataReader myreader;
try
{
connectionDatabase.Open();
myreader = cmddata.ExecuteReader();
MessageBox.Show("Done");
while (myreader.Read())
{
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I need when press on this button check if the insert name found messagebox appear tell my the name exists and prevent the add. If not tell me the insert Done. How i can do this.
Regards
The best place to have this sort of check is in the database itself. Almost all databases can set a UNIQUE constraint on a field. If you set the name column in the name_registry to be unique, the DBMS won't let you add a second entry with the same name, and an exception will be thrown. This will usually be the best way.
If the DB isn't in your hands and you can't set the column to be unique, you can use the suggestion that #FrancisDucharme and others and query the DB for the given name, and only call the INSERT if it returns 0 results:
SELECT COUNT(*) FROM name_registry WHERE [name] = 'TheName'
Note, though that there's no need to call ExecuteReader, not for this single-result SELECT statement, nor for the INSERT statement above - you should call ExecuteScalar, which will return the single-value result without loading a full-scale DataReader that you don't really need.
And lastly, as an addition to the answer, I can't in good conscience let you go on without pointing you in the direction of at least one tutorial about using parameterized queries in ADO.NET, which not only help prevent SQL injection attacks, but also help clean up the code and make it more readable, in my opinion. There are many out there.
Firstly, As previously stated: You have MAJOR SQL Injections visible...
Secondly, You should be using params.
Third, If you
SELECT *
FROM [TABLE]
WHERE [ColumnName] = #Param