I want to update a row by code in my form. Maybe some columns are null. So while command is
executing, an error will rise and say Incorrect syntax near the keyword 'where':
SqlCommand Update = new SqlCommand("Update Table_065_Turn SET Column02=" + Row["Column48"] + " , Column15= " + Row["Column15"].ToString() +
" where ColumnId=" + StatusTable.Rows[0]["ColumnId"], Con);
Update.ExecuteNonQuery();
I know this error will be displayed because Row["Column15"] is null.
How can I check if the column in datarow is null; of course without any extra variable or commands before Update command.
I mean check columns exactly in update command.
I would recommend using SqlParameters, also SqlCommand implements IDisposable so you should wrap it up in a using statement e.g.
using (SqlCommand update = new SqlCommand("Update Table_065_Turn SET Column02=#Col2, Column15=#Col15 where ColumnId=#ColId", con))
{
update.Parameters.AddWithValue("#Col2", Row["Column48"]);
update.Parameters.AddWithValue("#Col15", Row["Column15"]);
update.Parameters.AddWithValue("#ColId", StatusTable.Rows[0]["ColumnId"]);
update.ExecuteNonQuery();
}
Also you might be better actually validating the fields before you execute the query unless null is a valid column value.
I suspect you have to "replace" .net null with database keyword NULL, e.g.
string sql = "Column15 = " + (row[15] == null ? "NULL" : row[15].ToString()) in your case, but the much better way is to use Parameters as written by James, also keep in mind someone could provide hamful strings to your query:
row[15] = ";DROP DATABASE; --" would be enough in your case to cause all your data to be lost ;) (see "SQL injection" on your favorite search engine
you could use
var value = "" + Row["columnname"] ;
so ,you do not need to check the object is null
it is safe..
You could do like this:
string filterString;
if (StatusTable.Rows[0]["ColumnId"]!=System.DBNull.Value)
filterString= #" WHERE ColumnID= StatusTable.Rows[0]["ColumnId"]";//I assume the value returned is a string
else
filterString="";
And then you can just append the filterString variable to your SQLCommand string.
Related
I am stuck at one problem and I just can't solve this.
I get this Error:
Error Message
That's the relevant table
The Code:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = " + #departmentCB.Text;
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
The weird thing I don't understand is that a similar code works just fine without any error in my project:
query = "UPDATE LDV SET received = #received, department = #department WHERE Id =" + #idTxt.Text;
command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#received", inDT.Value);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.ExecuteNonQuery();
MessageBox.Show("Lungenautomat wurde aktualisiert");
If relevant, my connection string:
connectionString = ConfigurationManager.ConnectionStrings["SCBA_Manager_0._1.Properties.Settings.SCBAmanagerConnectionString"].ConnectionString;
I really hope you can help me :(
Thank you!
The department column is a text column, so comparing it to a value means the value should be wrapped in quotes.
// This fix is not the recommended approach, see the explanation after this code block
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + departmentCB.Text + "'";
// ^--------------------------^------ single quote added to wrap the value returned by departmentCB.Text
On the other hand, this error does not occur in your second example, because there you're correctly using the Parameters.AddWithValue() method to add the value for the #department parameter, and because id is a numeric column, so it doesn't require the value wrapped in quotes.
However, while the code shown above does the job, it is not the right way of doing the job. The correct way is to used parameters for all values to be injected into a query. The queries you've shown above are already correctly using parameters for some values (e.g. nextMaintenance in the first query, received and department in the second), but are incorrectly doing string concatenation for other values (e.g. department in the first query, id in the second).
Usage of Parameterized SQL
The benefit of using parameterized SQL is that it automatically takes care of adding quotes, prevents SQL injection, etc.
Therefore, its best to change your first code block to:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = #department";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
Notice how the string query is a single string without any messy concatenation, and that it contains two parameters #nextMaintenance and #department? And how the values for those parameters are correctly injected using Parameters.AddWithValue() in the following lines?
Your second code block can be similarly improved by using a parameter for the Id column.
query = "UPDATE LDV SET received = #received, department = #department WHERE Id = #Id ";
command.Parameters.AddWithValue("#Id", idTxt.Text);
Further Information
Do read up about SQL injection ( https://technet.microsoft.com/en-us/library/ms161953(v=sql.105).aspx ) to see how using string concatenation like your original code can lead to various security issues, and why parameterized queries are the preferred way of injecting dynamic values into SQL queries.
You can read up more about parameterized queries here: https://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx
In your first example, the WHERE clause evaluates to
WHERE department = Kasseedorf
wheras it should be
WHERE department = 'Kasseedorf'
So the line should be
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + #departmentCB.Text +"'";
It works in the second example, because id is an integer and doesn't neet quotes.
I have a problem while reading from SQL Server in C#. It is happening in SSIS, I have inserted a C# script in data flow.
I am using the code below:
using (SqlConnection connection = new SqlConnection(connectionString))
{
string vendorName = Row.VendorName.ToString().Substring(0,1).ToUpper() + Row.VendorName.ToString().Substring(1);
using (SqlCommand command = new SqlCommand("Select TOP 1 * from Logs where MessageId = '" + Row.id.ToString() + "'" +
"AND name = (Select Id from Names where vendor_name = '" + vendorName +
"order by CreatedDate desc", connection))
{
connection.Open();
string status = "";
using (SqlDataReader oReader = command.ExecuteReader())
{
while (oReader.Read())
{
status = oReader["Status"].ToString();
}
}
if (String.IsNullOrEmpty(status))
{
SaveDataToDB(Row.id, Row.VendorName, "Unknown");
}
}
}
In the Logs table, there are about 10000 rows, and the related datasource, where Row data belongs to, has around 9000 records. The problem is that, even though the query is working well, in the script it sometimes brings status value as null, because it cannot find the record in the SQL. I am getting the query and copy/pasting it to SQL, executing the query brings result there, but not in C# somehow. For example, I am running the C# two times in sequence, at the first time it says Status is null for the id: 354456, but when I run it at the second time it finds 354456 correctly but saying that status of 354499 is null.
Any idea for me to solve this issue? I really appreciate for any help.
According to me, this could be due to order of evaluation of user defined values embedded within the query. Could be the first dynamic value might be evaluated before the one in the inner query.
As I am not sure about the value to variable binding, however, I would recommend you to check following points;
a) externalise both your variable (vendor name and row id) outside and evaluate and ensure it has respective values
b) and then form your query statement with the evaluated values
May be you can debug and see the CommandText of command object just before call Execute.
your code is really inefficient, you should cache vendorname in a string and do all the substring operations on that.
for example:
string vendorName = Convert.ToString(Row.VendorName);
vendorName = vendorName.Substring(0,1).ToUpper() + vendorName.Substring(1);
instead of selecting all the columns, select the specific column for a speed up select Status from.
try to debug your code first, see which id you are getting and what is the result of your query.
its really hard to debug your code without any debug information.
change your code to this (Select Id from Names where vendor_name = '" + vendorName + "')" and put a blank space next to every " character e.g. " AND instead of "AND
My friend wants to transfer her data from the database to a textbox to her program in c# but it gets an error of this:
"Data is Null. This method or property cannot be called on Null
values."
Here is the code by the way:
sql_command = new MySqlCommand("select sum(lt_min_hours) as TotalLate from tbl_late where (late_date between '" + date_start + "' and '" + date_to + "' and empid = '" + empid + "')", sql_connect);
sql_reader = sql_command.ExecuteReader();
if (sql_reader.Read())
{
textBox_tlate.Text = sql_reader.GetString("TotalLate");
}
else
{
MessageBox.Show("No Data.");
}
From documentation;
SUM() returns NULL if there were no matching rows.
But first of all, You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
After that, you can use ExecuteScalar instead of ExecuteReader which is returns only one column with one row. In your case, this is exactly what you want.
textBox_tlate.Text = sql_command.ExecuteScalar().ToString();
Also use using statement to dispose your connection and command automatically.
You need to test for DbNull.Value against your field before assigning it to the textbox
if (sql_reader.Read())
{
if(!sql_reader.IsDbNull(sql_reader.GetOrdinal("TotalLate")))
textBox_tlate.Text = sql_reader.GetString("TotalLate");
}
EDIT
According to your comment, nothing happens, so the WHERE condition fails to retrieve any record and the result is a NULL.
Looking at your query I suppose that your variables containing dates are converted to a string in an invalid format. This could be fixed using a ToString and a proper format string (IE: yyyy-MM-dd) but the correct way to handle this is through a parameterized query
sql_command = new MySqlCommand(#"select sum(lt_min_hours) as TotalLate
from tbl_late
where (late_date between #init and #end and empid = #id", sql_connect);
sql_command.Parameters.Add("#init", MySqlDbType.Date).Value = date_start;
sql_command.Parameters.Add("#end", MySqlDbType.Date).Value = date_end;
sql_command.Parameters.Add("#id", MySqlDbType.Int32).Value = empid;
sql_reader = sql_command.ExecuteReader();
This assumes that date_start and date_end are DateTime variables and empid is an integer one. In this way, the parsing of the parameters is done by the MySql engine that knows how to handle a DateTime variable. Instead your code uses the automatic conversion made by the Net Framework that, by default, uses your locale settings to convert a date to a string.
Using C# on VS13 with a connected Access database and am receiving the error "No value given for one or more required parameters" when executing certain SQL.
Here is my code. Thanks for your time!
// ID accessors for an itemLine object
public void setID(string Value) { ID = Value; }
public string getID() { return ID; }
...
// Code snippet where error originates
foreach (CartItem itemLine in parBasket)
{
cmd.CommandText = "SELECT Instock FROM tblProducts WHERE ProductID = " + itemLine.getID() + "";
OleDbDataReader reader = cmd.ExecuteReader();
reader.Read();
int stock = Convert.ToInt32(reader["Instock"]);
stock = stock - itemLine.getQuanity();
reader.Close(); //Close the reader
cmd.CommandText = "UPDATE tblProducts SET InStock =" + stock + " WHERE ProductID = " + itemLine.getID() + "";
updated = updated + cmd.ExecuteNonQuery();
}
cn.Close();
return updated;
}
If CartItem.getID() returns an integer, not a string, then you need to remove the single quotes around it in the SELECT statement you are building.
Even better - read up on using SqlParameter and use this when building queries like this, as it helps avoid this sort of error, and also prevents SQL injection attacks, if any of the parameter data comes directly from user input.
To fix those errors yourself, you should:
Run with the debugger;
When the SQL command throws an exception the debugger should break (at least if it's unhandled. If you catch it, the debugger may still break but you have to tweak its config to do so);
Use a Watch or something to look at the CommandText of your SqlCommand (i.e. the SQL text that actually gets executed).
This should make pretty obvious what is wrong.
Now using my Crystal ball rather than a debugger, I think your problem is that getId() returns a string (per your comment on the question) and you end up with something like: WHERE ProductID = FortyTwo in both the first and second SQL queries.
The bad solution to this would be to enclose the string in quotes: WHERE ProductID = 'FortyTwo' but you should be careful that your ID doesn't contain a quote itself (which you should escape).
The good solution is to use a SQL parameter. Assuming SQL Server syntax: WHERE ProductID = #id and cmd.Parameters.AddWithValue("id", item.GetId()). (Note: you use the same command repeatedly, you should not add the parameter repeatedly. Rather, add it once and then change its value at each iteration.)
I usually create parameterized queries in order to avoid SQL Injection attacks. However, I have this particular situation where I haven't been totally able to do it:
public DataSet getLiveAccountingDSByParameterAndValue(string parameter, string value)
{
string sql = "select table_ref as Source, method as Method, sip_code as Code, " +
" from view_accountandmissed " +
" where " + parameter + " like #value " +
" order by time DESC ";
MySqlCommand cmd = commonDA.createCommand(sql);
cmd.Parameters.Add("#value", MySqlDbType.String);
cmd.Parameters["#value"].Value = "%" + value + "%";
MySqlDataAdapter objDA = commonDA.createDataAdapter(cmd);
DataSet objDS = new DataSet();
objDA.Fill(objDS);
return objDS;
}
As you can see, I am creating #value as a parameter but if I tried to do the same with parameter the query would fail.
So, is there a risk of SQL Injection with this query? Also, take into account that parameter is set by a DropDownList's SelectedValue (not a TextBox, so the input is limited). If so, how can I improve this query?
Yes there is:
" where " + parameter + " like #value " +
The value in parameter is your risk. In the postback you should check if the selected value is in the set of start values of the dropdown list.
Make the parameter an enum and pass the enum to your function. That will eliminate the risk (something like: not tested):
public DataSet getLiveAccountingDSByParameterAndValue(ParameterEnum parameter, string value)
.....
" where " + parameter.ToString() + " like #value " +
The ParameterEnum contains a list of all possible values in your dropdown list. In your code behind, parse the selected value to the enum.
So, is there a risk of SQL Injection with this query?
I think yes, it's vulnerable to SQL injection. For example, parameter = "1=1 OR value"
Also, take into account that parameter is set by a DropDownList's
SelectedValue (not a TextBox, so the input is limited)
Doesn't really matter. A malicious user can inject any value on the executable itself or on the network packet (and thus send a value that doesn't exist on the DropDown).
If so, how can I improve this query?
You should check parameter argument and compare with DropDown values. For more generic data, I think there should be libraries that check such things (but I have no C# idea...).
var columns = new [] {"column1", column2", ....};
if (!columns.Contains(parameter))
return or do something else
EDIT
The only SQL injection risk is by passing the column name in the where clause using string concatenation. There is no other way. The truly shield is to check that the column name is a valid one, it exists in the table.
Even ASP .Net has event validation (checks that the posted value is one of the dropdowns), you can't base on this since this protection can be disabled.
The parameter used with like is not object to SQL injection
Since 2.0 ASP.NET automatically validates postback and callback arguments to see if they differ. So this is a good example when it's useful to EnableEventValidation.
http://odetocode.com/blogs/scott/archive/2006/03/20/asp-net-event-validation-and-invalid-callback-or-postback-argument.aspx
You'll get following exception then:
"Invalid postback or callback argument"
You could ensure that it's set to true by explicitely setting it in codebehind, for example in Page's Init event:
protected void Page_Init( object sender, EventArgs e )
{
// don't remove this
Page.EnableEventValidation = True;
}
Edit: Oops, actually this setting cannot be changed from codebehind, it compiles but throws following runtime error:
The 'EnableEventValidation' property can only be set in the page
directive or in the configuration section.