1

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.

Dale K
  • 25,246
  • 15
  • 42
  • 71
Eugene Koen
  • 29
  • 1
  • 5

3 Answers3

3

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:

  1. you are mixing UI code with application code.
  2. you are using a global variable (or at the very least a field) to hold your instance of the SqlConnection.
  3. you are using terrible names for your parameters and texboxes.
  4. You are using AddWithValue.
  5. 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();
        }
    }
}
Cleptus
  • 3,446
  • 4
  • 28
  • 34
Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • Although for architectural related advice I would suggest codereview.stackexchange.com your fifth point is a really good point. Do note the OP update was wrong, check the edit in my answer because atm your answer would update the whole table. – Cleptus Jun 20 '19 at 09:55
  • @bradbury9 Good catch. I didn't notice that. Thanks! – Zohar Peled Jun 20 '19 at 10:21
  • Neither did I notice, until I got an edit suggestion ;-) – Cleptus Jun 20 '19 at 10:48
  • Regarding "connectionString should be obtained from configuration file" I would DI so integration testing could be done if necessary, but perhaps adding DI is too much to the answer, killing files with shotgun. – Cleptus Jun 20 '19 at 10:58
  • Yeah, I think a configuration file is enough for this point - also, even if you are using DI, it's still better to read the connectionString from a config file (and then inject to the dal class). – Zohar Peled Jun 20 '19 at 11:18
1

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 insert data

Update Data update data

Soumya Das
  • 89
  • 5
0

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();
Soumya Das
  • 89
  • 5
  • This is the biggest problem, but not the only one, the question has diferent problems and this answer only addresses one of them – Cleptus Jun 20 '19 at 09:59