I have C# forms application with a TextBox called ageTextBox;
After I press a button, the application should update one or more values from my MySQL database, but it doesn't.
Here's the code:
private void button2_Click(object sender, EventArgs e)
{
// this works perfectly if I put an actual number instead of #ag
string sql = "update persons set age = 30 where age = #ag";
OdbcCommand cmd = new OdbcCommand(sql, conn);
cmd.Parameters.Add("#ag", OdbcType.Int);
cmd.Parameters["#ag"].Value = ageTextBox.Text;
int aff = cmd.ExecuteNonQuery();
if (aff == 0)
MessageBox.Show("No rows afected");
else
MessageBox.Show("Everything went as planned");
}
}
It always shows "No rows affected"! (unless I enter something that's not a number, in which case, an exception is thrown)
Does anyone know what's going on?
cmd.Parameters.Add is obsolete you should use cmd.Parameters.AddWithValue try using this instead:
cmd.Parameters.AddWithValue("#ag", int.Parse(ageTextBox.Text));
it is possible that ODBC isn't reporting rowcounts (the equivalent of SET NOCOUNT ON). You might try adding a select ##rowcount as a final statement (or the equivalent for your particular DB), and using ExecuteScalar() (which will read that value).
This is not a solution but rather a debugging tip. Since you said it works ok when you enter a number in your code, can you try and fire a SQL tracer tool for MySQL (I suppose there is one available somwhere) and see what query is actually executed on the DB in the "working" and in the non working case (and in the exception case maybe)?
This can give you a good insight on what is going on...
Related
I'm trying to insert into a table in a C# program. I have this insert command:
var insertSql = #"INSERT INTO dbo.[Case]
VALUES (#Id, #IsDeleted, #CaseNumber, #ContactId, #AccountId, #ParentId, #SuppliedName, #SuppliedEmail, #SuppliedPhone, #SuppliedCompany, #Type, #RecordTypeId, #Status, #Reason, #Origin...
And then I've got many lines adding in the parameters like so:
var command = new SqlCommand(insertSql, easySoftConn);
if (case2.Id != null)
command.Parameters.AddWithValue("#Id", case2.Id);
else
command.Parameters.AddWithValue("#Id", DBNull.Value);
if (case2.IsDeleted != null)
{
if (case2.IsDeleted == "true")
command.Parameters.AddWithValue("#IsDeleted", 1);
else
command.Parameters.AddWithValue("#IsDeleted", 0);
}
else
command.Parameters.AddWithValue("#IsDeleted", DBNull.Value);
if (case2.CaseNumber != null)
command.Parameters.AddWithValue("#CaseNumber", case2.CaseNumber);
else
command.Parameters.AddWithValue("#CaseNumber", DBNull.Value);
if (case2.ContactId != null)
command.Parameters.AddWithValue("#ContactId", case2.ContactId);
else
command.Parameters.AddWithValue("#ContactId", DBNull.Value);
...
When I finally execute the insert:
try
{
command.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException e)
{
CLog.Write(e.Message.ToString(), CLog.ErrLvl.Error);...
}
I get the error:
String or binary data would be truncated
My issue is, the error doesn't tell me which column would be truncated. I've got 80 columns I'm inserting into, and I'd rather not go through them one-by-one. Is there a way to get the error handling to tell me exactly which field is throwing the error?
EDIT: I have a full stack trace in my log file but it still doesn't tell me which column, I just shortened it to the actual error here.
Switching to using strongly typed data access would head this one off sooner:
Add a dataset file to your project
Open it, right click the surface, add tableadapter, set connection parameters, add a query of SELECT * FROM [Case]
FInish the wizard, a datatable and adapter are generated. The DB is used to drive the creation, so all the string columns have a MaxLength property in the dataset that comes from the DB
Attempting to add a row to this table will now cause an error like "unable to set column XYZ, the value violates the MaxLength limit for the column"
Data access code looks like:
var dt = new YourDataSetNameHere.CaseDataTable();
dt.AddCaseRow(put, your, values, here, you , dont, need, to worry, about, null, this, or, data, type, that, because, VS, handles, it, all, for, you, in, the, DataSet.Designer.cs, file);
new YourDataSetNameHereTableAdapters.CaseTableAdapter().Update(dt); //save the new row;
So it'll save you a boatload of time writing boring data access code too
Depending on your SQL version you can apply a KB to get this to show more data as stated here - Link
It effectively starts to show messages like the following
Msg 2628, Level 16, State 6, Procedure ProcedureName, Line Linenumber
String or binary data would be truncated in table '%.*ls', column
'%.*ls'. Truncated value: '%.*ls'.
This came from this great post Link which goes much further to explain how you can try and search for the column should this not be possible. The post also talks about how you can do manual searching although I'd imagine if the list of columns is too large that may be something you want to avoid.
Looks like the value of one or more of your parameters has more length than the table cell can contain.
You should to look at table column definitions.
I have a problem in my program that's supposed to store projects given by the user in a database. I'm stuck on the edit project button. After entering new values in the program and hitting the button to save the values everything runs successfully with no errors. The messagebox that says "project edited" appears, I hit ok but the database stays the same. There is no error in the code, the SQL code that gets sent to update the database values is also correct but it doesn't work. Can anyone help with this because I am lost.
Here is the method that creates and executes the SQL code to update the database.
enter image description here
Wow man that code is wrong in so many ways according to code standards and principles most popular :) but that is not what the question is about directly, though getting you past lost we have to start at the basic tbh:
Suggestions
when you catch that exception if it comes, show that in a messagebox also you can even add an error icon as part of the .Show command, it's build in.
Move the connection.Close to the finally block instead of having it replicated
Consider making an SQL procedure instead and just parse the parameter into that, this code is prone to sql injection that you pose
Consider not making the procedure and familiarize Yourself with entity framework, it's going to make your life so much easier
do not concatenate like that, use interpolation or string.Combine or you'll be copying stuff all around on the stack, for each + a new copy one and two into third, it is super inefficient
When You write the code works and the sql is correct, consider that the outcome is not the desired and therefore it doesn't technically ;) the best and the worst about computers, is that they do what you ask.
Don't write Your DAL code in the form at all
Consider checking your parameters for default values
You do not have data to say 'project was updated' only 'values were saved', you do not check the old values in the code
Still besides that I do not see why what you wrote wouldn't work, provided the resulting sql is valid in what db you use, but i suppose if you do some of these things the error will present itself
I don't think it's a connection problem because I have a function that updates only the finishdate of the project and that works completely fine.
Here is the function:
public static MySqlCommand FinishProject(int projID, string finishdate) {
try {
if (connection != null) {
connection.Open();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#value", projID);
cmd.Parameters.AddWithValue("#finishdate", finishdate);
cmd.CommandText = "UPDATE `b1c`.`projects` SET `finishdate` = (#finishdate) WHERE (`projectid` = (#value));";
int i = cmd.ExecuteNonQuery();
connection.Close();
if (i != 0) {
MessageBox.Show("Project finalized.");
i = 0;
}
}
} catch (Exception ex) {
MessageBox.Show("Catch");
connection.Close();
}
return cmd;
}
You can see it's basically the same the only difference are the values.
So it shouldn't be a connection thing because this one works fine I think.
I also don't think it's a problem in the SQL database because all the problems
I've had up until now that had anything to do with the database have shown as errors in visual studio.
If anyone can help I will provide screenshots of anything you need and thank you all once again for trying to help.
Here is the screenshot of the function I've pasted previously so it's easier to look at.
finishprojectfunction
I'm trying to check if the user's HWID already exists in my 'users' table in mysql database when trying to login. And if it does exist, make another check. However I'm getting error called "You have an error in your SQL syntax; check the manual corresponds to your MySQL server version..." and so on. There doesn't seem to be any errors in the syntax. I think the issue would be in int part. I'm not sure so I need your help!
I've tried to google this and search on stackoverflow (here). I haven't found answer to my "actual" issue. I tried to use boolean instead of int.
MySqlCommand hwid = con.CreateCommand();
hwid.CommandType = CommandType.Text;
hwid.CommandText = "SELECT COUNT * FROM users where hwid='"+leet+"' AND username='"+textBox1.Text+"'";
hwid.Parameters.AddWithValue("hwid", leet);
hwid.Parameters.AddWithValue("username", textBox1.Text);
int hwidCheck1 = hwid.ExecuteNonQuery();
if ((int)hwidCheck1 > 0)
I want it to check if the hwid is in database and continue to next check.
You are missing (*) in your sql.
It should be some thing like this:
SELECT COUNT(*) FROM users where hwid='whatever' AND username='someuser'
This is exactly what the debugger is telling you, something is wrong with SQL syntax.
Another thing I have seen is, to check the count result you need to change your logic a bit.
from this:
int hwidCheck1 = hwid.ExecuteNonQuery();
if ((int) hwidCheck1 > 0)
to this:
int hwidCheck1 = int.Parse(hwid.ExecuteScalar().ToString());
if (hwidCheck1 > 0)
I was getting this error: "Input string was not in a correct format."
Here is my Code:
private void UpdatePOdetailBalance(int Qty)
{
int newbal;
SqlCommand com = new SqlCommand();
com.Connection = cn;
newbal = Convert.ToInt16(txtQtyOrdered.Text) - Qty;
com.CommandText =
"UPDATE PODetail SET BalanceQty="+ newbal +" WHERE OrderID=" +
Convert.ToInt16(txtPONumber.Text) + "AND ItemID=" +
Convert.ToInt16(txtItemNo.Text);
com.ExecuteNonQuery();
}
private void btnOK_Click(object sender, EventArgs e)
{
UpdatePOdetailBalance(Convert.ToInt16(txtQuantity.Text));
}
I want to compute the newbal which is equal to txtQtyOrdered minus Qty but i'm getting this error please help me with this. Thanks.
The problem stated by your error message is probably on one of the lines that try to convert the value in the textboxes to a short integer. Without any check, the value typed by your user could be anything but a number and you get this error message (for example, if you user leaves the textboxes empty).
You should try to check if the textboxes content could be converted to a valid short integer using TryParse before attempting to execute the query
int ordered;
if(!int16.TryParse(txtQtyOrdered.Text, out ordered))
{
MessageBox.Show("Invalid number for Ordered quantity");
return;
}
int orderID;
if(!int16.TryParse(txtPONumber.Text, out orderID))
{
MessageBox.Show("Invalid number for OrderId");
return;
}
int itemID;
if(!int16.TryParse(txtItemNo.Text, out itemID))
{
MessageBox.Show("Invalid number for ItemID");
return;
}
At this point you could execute your calculation using the converted short integers and then write your query in this way (adding a space before the AND)
com.CommandText =
"UPDATE PODetail SET BalanceQty="+ newbal.ToString() +
" WHERE OrderID=" + orderID.ToString() +
" AND ItemID=" + itemID.ToString();
But the string concatenation of query text and user input is never advised as a good practice (in your case is harmless because if the conversion is successful you don't have to worry about Sql Injection, but don't take the habit to do it).
So the perfect way to write this query is through the use of a parametrized query
com.CommandText =
"UPDATE PODetail SET BalanceQty=#newbal " +
" WHERE OrderID=#orderID " +
" AND ItemID= #itemID"
com.Parameters.AddWithValue("#newbal", newBal);
com.Parameters.AddWithValue("#orderID", orderID);
com.Parameters.AddWithValue("#itemID", itemID);
com.ExecuteNonQuery();
As a good article on Parameterized query and why to use them, I suggest to read these old words from Jeff Atwood
You need to put a space before your "AND" and that you are trying to convert a string to an integer that isn't an integer.
I'd recommend making changes according to the following code review suggestions based on the code (listed in order of value (cost/benefit of "fixing")):
This method, which is accessing a database should not be reading controls to get its values. Instead there should be an event handler, such as a button click, that parses the values of other controls, using TryParse, as gregjer answered. By segregating the UI and Data code, the data access layer is easier to test and by parsing at the surface (the UI layer) exceptions dealing with bad user input will be caught as soon as possible.
Dynamic SQL via strings in the database or in the data access layer w/i .NET is open to SQL injection. You are resolving that issue by parsing the text, so awesome job by you. BUT, this was already handled by the .NET team by providing parameterized commands. Refer to the MSDN SqlCommand.Parameters or see here for a brief, including how a consuming developer groks this topic: When should "SqlDbType" and "size" be used when adding SqlCommand Parameters?
Variable naming. Instead of Qty, standard .NET naming conventions would call for quantity, camelCased since it is a parameter and the full human language name, not a shorthand or abbreviation, especially for publicly visible bits. IntelliSense makes long variable names not a problem. Since .NET is unwieldy using just Notepad, it should be assumed that other developers are using an IDE such as VisualStudio or SharpDevelop, so use meaningful names.
Stored procedures should be used. Every time this SQL is executed, SQL Server needs to check its command cache minimally, but if the command has been flushed from cache, the SQL command needs to be interpreted and encached (put into cache). This as well as the fact that using a stored procedure requires "shipping" less bytes on every call to the database.
That error means that the string you're trying to convert is not an integer.
Try to use int.TryParse
int newbal;
if(int.TryParse(txtQtyOrdered.Text, out newbal))
newbal = newbal - Qty;
the same with other texts you are trying to convert
... and add space before " AND which will generate next error
I think you need to debug your code. During debugging copy your query from "com.CommandText" and paste in SQL Server you find the error
There is only a query error nothing else...
May be txtQtyOrdered value is not integer, there is also need blank space "AND ItemID=" to " AND ItemID="
Thanks,
Taha
First - You are missing a space before "AND"
You should try to parse the values before the update statement.
You should decide what you want to do in case the input from the textbox wasn't in the correct format rather then just get an exception when you try to update.
This isn't the right way to format strings, You should use string.Format
you can sometimes run into this problem when you have multiple parameters and are using Oracle or DB2 databases. They dont's support named parameters or it's not turned on.
Oracle:
Dim cmd As OracleCommand = DirectCast(connection.CreateCommand, OracleCommand)
cmd.BindByName = True
Make sure you parameters are added to the command object in the same order as the sql statement
I am facing a quite strange problem here.
My DAL was written using OdbcConnection objects and was perfectly working.
However I had to respect some requirements and therefore had to move the system to use MySqlConnection
Shouldn't give any problem, would you say.
However, there is a little misunderstanding now: when I execute an UPDATE command, without entering any new detail (let's say I change the user "test"'s username to... "test"), the command.ExecuteNonQuery() returns 1 anyway.
With the previous system & OdbcCommand objects, it returned 0 if no field changed.
Is it just a basic difference between the two systems or is there anything I've missed here?
Just some code even if it is very basic:
private readonly string _updateUserCommand =
"UPDATE user u " +
"JOIN city c ON c.Name=?City " +
"SET `City Id`=c.Id, u.Username=?Username WHERE u.Id=?Id";
// (...)
MySqlCommand command = null;
try
{
connection.Open();
//First step: storing the user in table user
//Creating the actual command:
command = new MySqlCommand(_updateUserCommand, connection);
command.Parameters.AddWithValue("?City", u.City);
command.Parameters.AddWithValue("?Username", u.Name);
command.Parameters.AddWithValue("?Id", u.Id);
int i = command.ExecuteNonQuery();
if (i != 0) return true;
else return false;
}
Your explanation doesn't make much sense. If you give a valid id and do an update on the username even if you update to the same name you can expect that 1 row will be affected. i.e. there is one row with the userId
ExecuteNonQuery returns the number of rows it changed, even if that change isn't noticeable to a human reader. The query did find a match and it did update one row. SQL really doesn't pay that much attention to what the old value was, it simply overwrites it and counts that as one row altered.