I'm getting a weird issue with substringing. Apparently the string I get can't be cast into an Int32 for some odd reason. The error message I get when I try doing that is "input string is not in correct format". Because of this, I can't insert these values into the Database either.
Here's the code...
string width = GetMetadata(filename, 162); //returns "1280 pixels"
string height = GetMetadata(filename, 164); //returns "700 pixels"
width = width.Substring(0, width.IndexOf(' ')); //returns "1280"
height = height.Substring(0, height.IndexOf(' ')); //returns "700"
//test: "System.Convert.ToInt32(width)" will fail, giving error "input string was not in correct format"
//doing the above on "width" yields the same result
//fails, giving error "no such column: 1280" (underlying database is sqlite)
Database.NonQuery("INSERT INTO image VALUES (" + fileid + ", " + width + ", " + height + ")");
For all the normal reasons - primarily avoiding leaving data conversions to the database, and preventing SQL injection attacks - I would suggest that you perform the parsing to a number in C#, and then use a parameterized query to talk to SQLite.
In this case, that will make it a lot easier to debug - either .NET will fail to parse the string as well (in which case it's likely to be a problem with your data) or it will work, and you won't need to worry about what conversions database was performing.
EDIT: I've just seen your comment saying that Convert.ToInt32 fails as well. That's a pretty clear indication that it's the data which is causing a problem.
I'd expect your code to look something like this:
string widthText = GetMetadata(filename, 162);
string heightText = GetMetadata(filename, 164);
widthText = width.Substring(0, width.IndexOf(' ')).Trim();
heightText = height.Substring(0, height.IndexOf(' ')).Trim();
int width = int.Parse(widthText, CulutureInfo.InvariantCulture);
int height = int.Parse(widthText, CulutureInfo.InvariantCulture);
using (SQLiteCommand cmd = Database.CreateCommand())
{
cmd.CommandText = "INSERT INTO image VALUES (?, ?, ?)";
cmd.Parameters.Add(fileid);
cmd.Parameters.Add(width);
cmd.Parameters.Add(height);
cmd.ExecuteNonQuery();
}
Note that the Trim call will remove any leading spaces, which it seems was the cause of the problem.
There may be some stray whitespaces in the string variables width and height. Invoke Trim() method on the strings before casting them into integers:
width = width.Trim();
height = height.Trim();
Hope this helps. Let us know.
Related
I am writing Insurance Managment System as project at University.
This is my MySQL commadn:
string lifeQuery = "insert into lifeinsurance values( null, '" + surname.Text + "." + pesel.Text + "', " + double.Parse(lifeInsSumTB.Text) + ", '" + double.Parse(lifeInsPriceTB.Text)
+ ");";
But te problem is that in UWP double is with ',' and to MySQL i need to have it with '.'.
When I try to do this like this: '25,453' it says data truncated. Without ' ', like this 25,453 it says that column count doesn't match value count at row 1, because it interets it as two different values 25 and 453.
So my question is:
How do I insert this double value to my table?
This problem is caused by the implicit conversion to a string when you call double.Parse and then concatenate the result back into the sql text. This requires the compiler to represent the double value as a string and it will use the current culture to do the conversion. Of course the result is not what MySql expect to be a double value.
Moreover using string concatenation to build sql commands leads to Sql Injection hacks. A very nasty problem that you should avoid. Always.
So let's try to add some code to resolve these problems
// A parameterized string without any concatenation from user input
string lifeQuery = #"insert into lifeinsurance
values( null, #surname, #sum, #price)";
MySqlCommand cmd = new MySqlCommand(lifeQuery, connection);
// Add the parameters with value for each placeholder in string
cmd.Parameters.AddWithValue("#surname", surname.Text + "." + pesel.Text);
// Parse the user input as a double using the current culture to correctly
// interpret the comma as decimal separator.
// Note that here I have no check on the correctness of the input. If your
// user cannot be trusted to type a valid double number then you should use
// the double.TryParse approach separating these lines from the actual check
cmd.Parameters.AddWithValue("#sum", double.Parse(lifeInsSumTB.Text, CultureInfo.CurrentCulture));
cmd.Parameters.AddWithValue("#price", double.Parse(lifeInsPriceTB.Text, CultureInfo.CurrentCulture));
cmd.ExecuteNonQuery();
Like other said - there are better ways to send over data with Sql. That being said this answer focuses on addressing your specific problem.
I think your problem may be your language/culture settings.
Try this:
Console.WriteLine(double.Parse("19.2323244").ToString("G", CultureInfo.InvariantCulture));
Output:
19.2323244
https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo?view=netcore-3.1#Invariant
I'm trying to save a set of records in my desktop c# application and it appears to stop since one of the values was in the incorrect format.
Before saving, the system goes through these computations:
private void ComputeTotalWeight()
{
double TotalWeight;
TotalWeight = ((Convert.ToInt32(txtSmall.Text)) + (Convert.ToInt32(txtMedium.Text)) + (Convert.ToInt32(txtLarge.Text)) +
(Convert.ToInt32(txtExtralarge.Text))) * .285;
txtTotalweight.Text = String.Format("{0:#,##0}", TotalWeight);
}
private void ComputeTagsCollars()
{
int TagsCollars;
TagsCollars = Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text)
+ Convert.ToInt32(txtLarge.Text) + Convert.ToInt32(txtExtralarge.Text);
txtTags.Text = String.Format("{0:#,##0}", TagsCollars);
txtCollars.Text = String.Format("{0:#,##0}", TagsCollars);
}
But once I save, it seems to be having a problem with the GrandTotal computation:
I suspect the error come from this computation:
private void ComputeGrandTotal()
{
double GrandTotal;
GrandTotal = (((Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text) + Convert.ToInt32(txtLarge.Text) +
Convert.ToInt32(txtExtralarge.Text)) * .285) * 315);
double TagsCollars;
TagsCollars = Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text) + Convert.ToInt32(txtLarge.Text) + Convert.ToInt32(txtExtralarge.Text);
txtTags.Text = String.Format("{0:#,##0}", TagsCollars);
txtCollars.Text = String.Format("{0:#,##0}", TagsCollars);
lblGrandtotal.Text = String.Format("{0:#,###,##0}", (GrandTotal + TagsCollars + TagsCollars));
}
I've tried commenting out all GrandTotal related values and functions, and the records begin to save with no problem. Here's a copy of my save function:
private void InsertNewRecord()
{
SqlCommand cmdInsert = new SqlCommand();
cmdInsert.Connection = cn;
cmdInsert.CommandType = CommandType.Text;
//cmdInsert.CommandType = CommandType.StoredProcedure;
cmdInsert.Parameters.AddWithValue("#QtySmall", Convert.ToInt32(txtSmall.Text));
cmdInsert.Parameters.AddWithValue("#QtyMedium", Convert.ToInt32(txtMedium.Text));
cmdInsert.Parameters.AddWithValue("#QtyLarge", Convert.ToInt32(txtLarge.Text));
cmdInsert.Parameters.AddWithValue("#QtyExtralarge", Convert.ToInt32(txtExtralarge.Text));
cmdInsert.Parameters.AddWithValue("#QtyTags", Convert.ToInt32(txtTags.Text));
cmdInsert.Parameters.AddWithValue("#QtyCollars", Convert.ToInt32(txtCollars.Text));
cmdInsert.Parameters.AddWithValue("#TotalWeight", Convert.ToInt32(txtTotalweight.Text));
cmdInsert.Parameters.AddWithValue("#NoWorkers", Convert.ToInt32(txtWorkersno.Text));
cmdInsert.Parameters.AddWithValue("#NoMachines", Convert.ToInt32(txtMachinesno.Text));
cmdInsert.Parameters.AddWithValue("#BomStatus", SqlDbType.VarChar).Value = txtStatus.SelectedItem.ToString();
cmdInsert.Parameters.AddWithValue("#StartDate", SqlDbType.DateTime).Value = dtpStart.Value;
cmdInsert.Parameters.AddWithValue("#EndDate", SqlDbType.DateTime).Value = dtpEnd.Value;
cmdInsert.Parameters.AddWithValue("#GrandTotal", Convert.ToInt32(lblGrandtotal.Text));
cmdInsert.CommandText = " INSERT INTO BillOfMaterials2 " + " (QtySmall, QtyMedium, QtyLarge, QtyExtralarge, QtyTags, QtyCollars, TotalWeight, NoWorkers, NoMachines, BomStatus, StartDate, EndDate, GrandTotal) VALUES (" + "#QtySmall, #QtyMedium, #QtyLarge, #QtyExtralarge, #QtyTags, #QtyCollars, #TotalWeight, #NoWorkers, #NoMachines, #BomStatus, #StartDate, #EndDate, #GrandTotal)";
//cmdInsert.CommandText = "spInsertBom";
cmdInsert.ExecuteNonQuery();
}
Any help would be much appreciated.
1st I would not attempt converting a textbox.text value to Int32 using convert; better to use
Int32 myint = 0;
Int32.TryParse(textbox.text, out myint) ;
This ensures that the text can be converted to an integer and if not you get 0 as a returned out value.
Then in your save method - your #GrandTotal parameter is trying to save to what datatype ? - what is the type in your database ?? do they match - if not you will get a format exception your data (Type) is not the same Format (type) as the column type.
The op does not have a valid number in the text box he has this:
lblGrandtotal.Text = String.Format("{0:#,###,##0}",
(GrandTotal + TagsCollars + TagsCollars));
This is why the code where he sets the parameter value = lblGrandtotal.Text it is not a number it has formatting commas etc.. he needs to remove those to make it work, using Int.TryParse would easily reveal this.
its starts here - first you are putting decimal in your format string with comma.
lblGrandtotal.Text = String.Format("**{0:#,###,##0}**", (GrandTotal + TagsCollars + TagsCollars));
also, later in your code you are storing INT int database, when its actually decimal.
and as mentioned by ken, use tryparse to convert the value from string to ....
There are several issues here some of the other posts touched on but, what really stands out (in my opinion) is you're not validating the input data, which is risky for many reasons, asking for future headaches and causing these issues. Also, there are standard numerical input controls you could use. If there's some reason you can't use them though, you should be validating the input and, if the data is not valid, handle it. Below is a quick way to validate and then handle invalid inputs.
private void ComputeGrandTotal()
{
//Since there are values that need to be validated and converted to integers for use in two calculations...
int smll, mdm, lg, xl;
//Validate the inputs can be converted and set the appropriate variable values at the same time
if (Int32.TryParse(txtSmall.Text, out smll) //using TryParse sets the integer variable values only if they can successfully be converted
&& Int32.TryParse(txtMedium.Text, out mdm)
&& Int32.TryParse(txtLarge.Text, out lg)
&& Int32.TryParse(txtExtralarge.Text, out xl)
)
{
int ttl = smll + mdm + lg + xl;
double GrandTotal, TagsCollars;
TagsCollars = ttl;
GrandTotal = TagsCollars * .285 * 315;
txtTags.Text = $"{TagsCollars:#,##0}"; //Resharper suggested simplification of String.Format("{0:#,##0}", TagsCollars)....I believe ReSharper
txtCollars.Text = $"{TagsCollars:#,##0}";
lblGrandtotal.Text = $"{(GrandTotal + TagsCollars + TagsCollars):#,###,##0}";
}
}
This will get the job done but it's pretty inflexible. Each input has to successfully convert to an integer or this will fail. A better, more time consuming approach would be something like this:
int smll;//, mdm, lg, xl;
try
{
smll = Convert.ToInt32(txtSmall.Text);
}
catch (FormatException)
{
smll = 0;
//txtSmall.Text value can't be converted to an integer
}
catch (Exception)
{
//some other issue occurred and you're probably better off just exiting entirely
return;
}
There are more flexible approaches out there, such as using number styles and such but, their flexibility comes at the price of you having to be more aware of the impact of what and how you're coding. Sometimes it's just safer to train your customers than write code you're not confident with.
You are trying to parse an integer in lblGrantotal.Text, but you are getting a FormatException, which means the text in lblGranTotal isn't recognized as a number. Maybe you are using comma , instead of point . as decimal separator, or something like that.
I have a problem, and I can not solve it for several days.
My simple code (dll file):
public static string RegisterUser(string table, string l, string p)
{
string query = "INSERT INTO " + table + "(login, password) VALUES ('" + l + "','" + p + "');";
return query;
}
Everything works fine, but when I want to write the data received on the network, it does not return the full string, for example
string recieveStr = Encoding.UTF8.GetString(connection.Buffer); //received text: RegUser:nipercop:passwrd
string[] strArr = recieveStr.Split(':');
if (strArr[0].Equals("RegUser"))
{
// string p = strArr[2]; //passwrd
// sendStr = SqlCommands.RegisterUser("users",strArr[1], "passwrd");//works fine
sendStr = SqlCommands.RegisterUser("users",strArr[1], strArr[2]); // doesn't work
}
returns message like that:
INSERT INTO users(mail, password) VALUES ('nipercop','passwrd
Lost end of the string " ') ". No errors, no warnings, no anything.
I tried change encoding to ASCII on server, on client, but no effect.
Try using string.Format:
string.Format(#"INSERT INTO {0}(login, password) VALUES ('{1}','{2}');", table, l, p);
Though ideally you should be using prepared SQL statements. The reason being is any of the values you insert could contain unexpected values, which leaves your code vulnerable to SQL injection and a plethora of other bad things.
For example if strArr[2] would contain " here, it'd cut off the query and result in an error. As it stands, your code doesn't account for that while seemingly accepting input from a remote socket. That's something you may want to revise.
The problem is was reading strings from the buffer.
Encoding.UTF8.GetString(connection.Buffer,0 , bufferLength);
Because the buffer length was (for example) 1024 bytes, and my last variable was length of the remainder of the length of the other variables, ie contained a empty symbols (spaces). Fail.
The question is simple.
I have a column in my database of data type NVARCHAR(20) .. so when I try to enter a data in this column that's for example contains 22 characters, it just ignores the last 2 characters instead of Throwing an exception!
Is this is normal ? .. and how to secure the database from such an issue ?
P.S: Of course I use validation controls and server validation, but how do I secure the database, is there's some kind of an advanced constraint more than just specifying the column's length, so it throws an exception and not accept the entered date ??
Edit
try
{
using (SqlConnection conn = new SqlConnection(ConnStr))
{
string Command = "SET NOCOUNT ON; INSERT INTO [Countries] (CountryName, IsVisible) VALUES (#Name, #IsVisible);";
using (SqlCommand comm = new SqlCommand(Command, conn))
{
comm.Parameters.Add("#Name", System.Data.SqlDbType.NVarChar, 20);
comm.Parameters["#Name"].Value = Name;
comm.Parameters.Add("#IsVisible", System.Data.SqlDbType.Bit);
comm.Parameters["#IsVisible"].Value = IsVisible;
conn.Open();
comm.ExecuteNonQuery();
return "Successfully added " + Name + " to the countries.";
}
}
}
catch (SqlException SqlEx)
{
string ErrorMessage = "";
for (int i = 0; i < SqlEx.Errors.Count; i++)
{
ErrorMessage += SqlEx.Errors[i].Number + " : " + SqlEx.Errors[i].Message + "\n";
}
return ErrorMessage;
}
Well this is the the code I'm using, and btw I just tried to insert a bigger data than the column's length directly from the Sql Management Studio and it actually displayed the message you were just describing!
The problem is between these two lines.
comm.Parameters.Add("#Name", System.Data.SqlDbType.NVarChar, 20);
comm.Parameters["#Name"].Value = Name;
SQL Server NEVER gets to see more than 20 chars. .Net is doing the truncation.
I can't remember the documentation for SQLParameter, a Google search was faster.
http://www.eggheadcafe.com/software/aspnet/30873895/sqlparameter-question.aspx
Now if you DID specify the SqlParam's length to be 40, then .NET will
automatically truncate the string to 40 characters for you.. and no
error will be raised (but you may loose data and not know it).
There are several areas that will truncate the data prior to it reaching the table without generating errors.
As #Richard pointed out for your specific case it is being truncated by ADO.
If you called a stored procedure that had a varchar(20) parameter and passed it 22 characters of data, the parameter processing engine in SQL server would truncate it as well.
However, if you wrote an insert state that tried to directly stuff 22 characters into a varchar(20) column, then you would see the error.
Basically, the parameter processing piece is silently "fixing" it. Whereas if you get rid of the parameters then it will fail. Which is why this question really is an exact duplicate of the one #gbs referenced.
Obviously, getting rid of the parameters is FAR from ideal.
I know we should never do this:
string select = "SELECT * FROM table1 ";
string where = "WHERE Name = '" + name + "' ";
string sql = select + where;
//execute the sql via ADO.NET
because of sql injection, because name can contain the char ', because of another 100 reasons. But now I have to do something similiar. I have a Dictionary<string, object> whose data look like:
Key(string) Value(object)
"Name" "Bob" //string
"ID" 10092L //long
"Birthday" 1980-05-07 00:00:00 //DateTime
"Salary" 5000.5m //decimal
//some others, whose key is a string, and value is string/long/int/DateTime/decimal
I want an easy way, to get all items in the dictionary collected in a String, just like a where statement:
Name = 'Bob' and ID = 10092 and Birthday = '1980-05-07 00:00:00' and Salary = 5000.5
String and DateTime are quoted with ', but note that the Name can be O'Neal. Is there any easy implementation? Input the dictionary, and return the string as a result.
EDIT Note that what I want is the string, I'm not going to execute it, parameterized command doesn't help. I just want a string that looks like a perfect safe WHERE statement.
The first code is only a problem if name is something entered by the user. Otherwise, it should be fine.
I don't know that it eliminates all problems but you might try experimenting with something like name = name.Replace("'", "''"). By converting all single quotes to double single quotes, you prevent the type of problems you described. Another approach might be to remove any single quotes.
However, the best route is to use query arguments. ADO supports these nicely and that would also eliminate any possibility of injection attacks.
The easy way could be like this:
string results = string.Join(" and ", myDict.Select( x=> x.Key + " = " + x.Value));
This of course wouldn't solve the quotes ' issue depending on different datatypes so you cannot use this as input to a SQL query - for that I would strongly recommend named parameters anyway - but is otherwise correct depending on the ToString() implementation of the values in your dictionary.
I wrote this many years ago, and always use it, and never ever have to think about this again. it is a waste of brain cells to solve this more than once:
// replace things like:
// O'Keefe with
// 'O''Keefe'
// make sure you don't call this twice!
static public string SqlString(string strInputSQL)
{
string strOut;
strOut = strInputSQL;
strOut = strOut.Replace ("'", "''");
strOut = "'" + strOut + "'";
return strOut;
}
Use it like this:
string sql = "SELECT * FROM FOO WHERE Name LIKE " + SqlString(myvalue);
There may be a dozen other ways to do it, but if you can have one and only one function, and use it consistently, you will save alot of time.
Try this link : Creating safe SQL statements as Strings
Some people consider this over-engineered, or just labourious to type. I fall back on a simple argument though...
Someone has already invested time and effort ensuring arguements can be safely and reliably included in SQL statements. Are you 100% certain you have pre-empted every possible scenario? Or is it more likely tried and tested code is more reliable?
But, then, I'm a bit anal ;)
var sb = new StringBuilder();
var isFirst = true;
foreach (var element in dic)
{
if(!isFirst)
sb.Append(" AND ");
else
isFirst = false;
sb.Append(element.Key);
sb.Append(" = ");
if(element.Value is decimal)
sb.Append(CastToSqlDecimalString((decimal)element.Value));
else
sb.Append("'" + String.Format(CultureInfo.InvariantCulture, "{0:G}", element.Value).Replace("'", "''") + "'");
}
You might want to handle decimals using this function
public static string CastToSqlDecimalString(decimal dec)
{
var sqlDecimal = new System.Data.SqlTypes.SqlDecimal(dec);
return string.Format("CAST({0} AS DECIMAL({1}, {2}))",
string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:G}", dec),
sqlDecimal.Precision,
sqlDecimal.Scale);
}