Error on Inserting data into database - c#

I have 3 Textboxes called, TxtFirstName, TxtMiddleName and TxtLastName. I would like to insert this info to the database where my column name is just FullName. I would want my 3 Information to join to insert them into one. What I did is :
string _fullname = _lastname + "," + _firstname + middlename;
cmd = new SqlCommand("INSERT INTO TableVote (FullName) VALUES ('" + _fullname + "')", sc);
but it seems that it gets me an error.
"String or Binary data would be truncated. The Statement has been terminated."
How do i correct this ?

The error means that your string is longer than the maximum length allowed for the column. You either need to adjust your schema to allow longer values or truncate the value you insert.
Also: You should really use parametrized commands:
cmd = new SqlCommand("INSERT INTO TableVote (FullName) VALUES (#fullname)");
cmd.Parameters.AddWithValue("#fullname", _fullname);
Read up on Sql Injection attacks.
Update: As mentioned by others you should contemplate storing the name in different columns (i.e. FirstName, MiddleName, LastName. Otherwise you throw away information which will be hard to recompute (e.g. try making a statistic of the most common middle name with your schema).

As already noted, the value you are inserting into your table is too long for the column specification.
HOWEVER
I've been working with databases for quite a while now, and I'd like to advise you to please not store the name all in one column. I've seen this over and over again. It seems like a good, quick idea at the time, but sooner or later, you'll have a requirement where you need to get just part of the name. Once you reach that point, you'll find yourself with all kinds of problems, because names are very, very complicated things that are highly dependent on language and culture. Given a list of whole names, think about how you would parse out just the last names. At first, it seems very simple, until you consider all the special cases, like people with two last names ("Harper-Smith"), last names from other cultures ("St. James", "O'Connell", "Van der Wall"), etc.
Just consider saving the name in three parts in three columns, it doesn't take much and it will save you a lot of trouble later.

Related

Is QuoteIdentifier enough to protect query from Sql injection attack?

While parameter is the best way to guard against Sql injection, there are times which we can't use it while building dynamic query. For example Table/Column/Index names cannot be passed in as parameter but only plain Text.
It seems like
SqlCommandBuilder.QuoteIdentifier
is the only option that I can find. Is calling this method enough to protect ourselves?
MSDN DOC:
Given an unquoted identifier in the correct catalog case, returns the
correct quoted form of that identifier. This includes correctly
escaping any embedded quotes in the identifier.
For example is
"Select * FROM " + SqlCommandBuilder.QuoteIdentifier("CustomTable" + userInputText);
safe to do?
Edit: The query is just an example. I am interested in finding out if Sql injection is ever possible.
It won't protect you from the attacker going to tables you don't want them to.
such as SQL system tables...
That may not be not safe to do.
To protect against any possible security problem with the SqlCommandBuilder.QuoteIdentifier method all that you need to do is get a list of the available table names etc. from the database and validate the user input against them.
Edited to add: I have reason to doubt if QuoteIdentifier is completely safe: the documentation for the SqlCommandBuilder.QuoteIdentifier Method says (as you previously quoted):
Given an unquoted identifier in the correct catalog case, returns the correct quoted form of that identifier. This includes correctly escaping any embedded quotes in the identifier.
Nowhere in that documentation does it state what happens if it is given an unquoted identifier in the wrong catalog case (whatever a "catalog case" is). Or what happens if the identifier is longer than the maximum allowed. Of course, undefined behaviour cannot be relied on.
Seems safe to do to me. Also, this might be a good reference:
Sanitize table/column name in Dynamic SQL in .NET? (Prevent SQL injection attacks)
Using user input in table name fields is never safe no matter how much you try to check it (unless you restrict the entries to a limited set of names or do some other kind of sorcery).
Even removing quotes, the user could type: TableName; DROP DATABASE db; or (SELECT * FROM <sensible table).
Possible solutions would be:
Using a ComboBox or an equivalent where the user can't modify the options
Check the input against a String[] with all allowed table names (where they should be identical to one of the entries)
and if the user input as a table name was just an example but you're going to use the input as part of a WHERE clause of a SELECT, then you should check Bobby Tables.
Quoted from the website:
From the C# Online wiki page ASP.NET Security Hacks--Avoiding SQL Injection
SqlCommand userInfoQuery = new SqlCommand(
"SELECT id, name, email FROM users WHERE id = #UserName",
someSqlConnection);
SqlParameter userNameParam = userInfoQuery.Parameters.Add("#UserName",
SqlDbType.VarChar, 25 /* max length of field */ );
// userName is some string valued user input variable
userNameParam.Value = userName;
Or simpler:
String username = "joe.bloggs";
SqlCommand sqlQuery = new SqlCommand("SELECT user_id, first_name,last_name FROM users WHERE username = ?username", sqlConnection);
sqlQuery.Parameters.AddWithValue("?username", username);
Have you considered using OData? you can pass in text, selecting tables, indexes and so on. But with OData you select what tables you want to publish this way, and it can't be injection attacked as you have to explicitly allow update and insert operations.
http://www.odata.org/documentation/odata-version-2-0/uri-conventions/
https://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api

C# error : Input string was not in a correct format

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

Is there a way to determine which column causes error?

I am working on a C# project that gathers data from different sources and stores the data in a SQL Server database. I sometimes get String or binary data would be truncated. error which is very annoying. I want to determine which column causes this error and log it. Is there another way than checking parameter lengths?
What I did is if the column is varchar(50), checking if data length is greater than 50. I feel like this is like a walkaround and wonder if there is any other neat solution.
Edit
if(data1.Length>50) logIt("col1, data1, condition");
else if(data2.Length>80) logIt("col2, data2, condition");
else {
SqlParameter p1 = (new SqlParameter("#p1", DbType.String));
p1.Value = string.IsNullOrEmpty(data1) ? SqlString.Null : (object)data1;
s1.Parameters.Add(p1);
SqlParameter p2 = (new SqlParameter("#p2", DbType.String));
p2.Value = string.IsNullOrEmpty(data2) ? SqlString.Null : (object)data2;
s1.Parameters.Add(p2);
s1.ExecuteNonQuery("UPDATE mytable SET col1=#p1,col2=#p2 WHERE condition=#condition");
}
void logIt(string p){
using (StreamWriter writer = new StreamWriter("log.txt"))
{
writer.WriteLine("Caused by:"+ p);
writer.WriteLine(DateTime.Now);
writer.WriteLine("--------------------------------------------");
}
}
Personally I would always check all of my parameters against the table that contains the data they will be used in. If the table says varchar (50) my parameter should be no longer than 50. I never write a stored proc without doing this and I presume you can simlarly limit parameter defintion on the c# side. Don't allow a user to type in more information than your table field can accept.
If you are pulling data from other databases or Excel spreadsheets, then you may have a differnt set of problems.
First you need to examine the incoming data carefully and determine if the column size you currently have is too small for the needed content. I find often when the data is too large, the information in the field is garbage and should be thrown out (things like notes about a contact being placed in the email field for instance instead of an email). You may even want to consider if you shoud be validating the input data in some fields.
If you need to keep the size and you aren't concerned about losing the extra characters, then you need to cast the data to the correct size before inserting it into your database or you need to create an exception process that will pull out the records which cannot be sent to the database into an exception table and return them to the people who own the orginal data to be fixed.
As you mentioned, In one of the INSERT statements you are attempting to insert a too long string into a string (varchar (50)) column.
You have to increase the column size to 50+ or 100, to fix the issue. To find which column is causing this issue is put some logs or run SQL Profiler or Enable All exceptions from VS -> Debug Menu -> Exceptions (Check all exceptions).

Cant insert character values from textbox to database

Trying to Insert into database from textboxes, but it will only accept integers and not characters - what could the problem be?
string sCMD = string.Format("INSERT INTO [Table] ([Item], [Des1],[Des2], [Prodline], [ANR], [STime]) VALUES({0},{1},'0',{2},{3},{4})"
,txtText1.Text, txtText2.Text, txText3.Text, txtText4.Text, txtText5.Text);
The name "" is not permitted in this context. Valid expressions are constants, constant expressions, and (in some contexts) variables. Column names are not permitted.
Here's something to consider:
using (SqlConnection c = new SqlConnection(connString))
{
c.Open();
using (SqlCommand cmd = new SqlCommand("INSERT INTO [Table] ([Item], [Des1],[Des2], [Prodline], [ANR], [STime]) VALUES(#Item,#Des1,'0',#ProdLine,#ANR,#STime)", c))
{
cmd.Parameters.AddWithValue("#Item", txtText1.Text);
cmd.Parameters.AddWithValue("#Des1", txtText2.Text);
cmd.Parameters.AddWithValue("#ProdLine", txText3.Text);
cmd.Parameters.AddWithValue("#ANR", txtText4.Text);
cmd.Parameters.AddWithValue("#STime", txtText5.Text);
cmd.ExecuteNonQuery();
}
}
If you were to send the SQL statement you're building to the server it might look something like this:
INSERT INTO [Table] ([Item], [Des1],[Des2], [Prodline], [ANR], [STime])
VALUES(Item Name,Here is my description.,'0',Prod line,ANR value,some time)
Clearly that's going to fail. It's not even surrounding the values with single quotes. Now, all of them may not be character, I get that, but you get my point.
But to add insult to injury it's wide open to SQL Injection the way you have it written. With the parameterized approach it's not.
This is a very dangerous line of code.
For starters, you are creating a string with no delimiters whatsoever for the values, which means that the only values that could pass withour issue are integers. Anything else needs to be delimited, which you don't do in this String.Format statement.
Worse, you are trying to create a sql statement from direct user input. Any weird value can cause your code to fail, or worse, cause execution of unwanted code. Imagine what would happen in a user entered eg 'sdf','sdfs',sdf' as the FIRST value. The resulting string would have 3 correct values for the first three columns that came from the first textboxt.
Now image what would happen if the user entered something like 'blablabla';DROP TABLE sometable;--. This would cause the Delete command to execute. This is a standard SQL injection attack scenario.
There are many more problems, eg when you try to pass numbers with floats, dates or any type whose conversion to string depends on your locale.
Instead of trying to construct a SQL query by string concatenation, you should use parameterized queries, as described in Give me parameterized SQL or give me death. The resulting code is easier to write, performs much faster, has no conversion errors and is not subject to SQL injection attacks,

"Input string was not in a correct format." calling int.Parse on a SQL Statement

I am getting the error:
"Input string was not in a correct format."
Note: If I change line 182 to an actual number in quotes (ie; "3" or "875"), and comment out line 171, this code works perfectly fine. However, "{7}", in line 174 is a field that is supposed to auto-increment, but wont. So I am trying to get a "number" in line 171, that will use the number of rows, + 1, to do the auto-=increment.
Any takers on this one? :-)
171 string rowCount = string.Format("SELECT COUNT(*) FROM Log WHERE Location is NULL");
173 string sql = string.Format("insert into Log values " +
174 "('{0}','{1}',{2},{3},'{4}',#{5}#,'{6}','{7}')",
175 comboBox1.Text,
176 comboBox2.Text,
177 float.Parse(textBox1.Text),
178 float.Parse(comboBox3.Text),
179 textBox3.Text,
180 textBox2.Text,
181 addRemove,
182 int.Parse(rowCount)
183 );
Stop using that code immediately and use parameterized SQL instead. Otherwise you're vulnerable to SQL injection attacks, as well as potentially having data type conversion issues.
Next, think about what you've actually got in rowCount. It isn't a string representing an integer - it's some SQL. Trying to parse that with int.Parse isn't going to work, is it?
You'd need to execute the query first - or use a subquery within your insert statement. To be honest, if it's meant to be an auto-incrementing field, I would just concentrate on getting that working rather than fudging round it with code which is going to be vulnerable to race conditions.
int.Parse(rowCount) converts string to number, e.g. "100500" to 100500. But your string contains "SELECT COUNT(*) FROM Log WHERE Location is NULL" and this is not a number.
string.Format is not going to execute your SQL commands. So int.Parse sees exactly "SELECT COUNT(*) FROM Log WHERE Location is NULL", which of course is not a decimal representation of a number.
Virtually all databases have native support for auto-incrementing columns. You should not be trying to use an int column and increment it yourself. There are all sorts of race conditions, performance issues, etc. to make an incrementing column really robust, and the database designers have already taken care of all of that for you.
You are probably looking for an answer that will fix your problems with this particular posting. The answers that have been posted will help you do that.
You should examine other approaches.
Use a command object and use parameters (suggested by #JonSkeet)
Do some research on how auto increment columns work. This varies by database vendor. It appears that you may be using Microsoft Access. For MS Sql Server the auto-increment column is an identity column and in Oracle, the mechanism is a bit different again, using sequences. Basically, you do not supply values for auto-increment columns, you let the database engine handle that for you. (also mentioned by a previous poster)
I would also suggest that you assign the values of your text boxes to variables and do some validation of the data before putting into your insert statements or parameters. Try to program defensively.

Categories

Resources