I am designing a database app, and have a form which populates with data from a sql database. If a user double clicks on any of the text boxes on the form they are able to change the value using an input box, which then executes the following code to update the database.
private void ProcessChanges(string strField, string strCurrentValue)
{
//...Connect To Database...//
string strCaseNo = txtCaseNo.Text;
string strConnect = BuildConnectionString();
SqlConnection linkToDB = new SqlConnection(strConnect);
linkToDB.Open();
//...Request User Input New Value...//
string strMessage = "Enter ammended details and click OK," + Environment.NewLine +
"or click Cancel to exit.";
string strInput = Interaction.InputBox(strMessage, "Case Details", strCurrentValue);
//...Send User Input to Database...//
string commandText = "UPDATE tblCases SET #FieldVal = #InputVal WHERE CaseNo = #CaseNoVal;";
SqlCommand sqlCom = new SqlCommand(commandText, linkToDB);
sqlCom.Parameters.Add("#FieldVal", SqlDbType.Text);
sqlCom.Parameters.Add("#InputVal", SqlDbType.Text);
sqlCom.Parameters.Add("#CaseNoVal", SqlDbType.VarChar);
sqlCom.Parameters["#FieldVal"].Value = strField;
sqlCom.Parameters["#InputVal"].Value = strInput;
sqlCom.Parameters["#CaseNoVal"].Value = strCaseNo;
int intQuery = sqlCom.ExecuteNonQuery();
MessageBox.Show(intQuery.ToString());
}
The problem is the database does not update at all. I know the connection is ok because the same ConnectionStringBuilder is used throughout my app. I have also added the messagebox at the end which tells me the return value of ExecuteNonQuery() which is '1', so that suggests a row has been updated. However nothing changes in my database and its really annoying me now.
You can't use variables for column names. You have to construct your sql string that the column names are embedded into the string.
string commandText =
"UPDATE tblCases SET [" + strField + "] = #InputVal WHERE CaseNo = #CaseNoVal;"
But you have to check the value of strField for sql injection attacks.
If you update the CommandText line as follows:
string commandText = "UPDATE tblCases SET #FieldVal = " + strField + " WHERE CaseNo = #CaseNoVal;";
and remove the lines
sqlCom.Parameters.Add("#FieldVal", SqlDbType.Text);
sqlCom.Parameters["#FieldVal"].Value = strField;
Be aware though that by doing this you are potentially opening yourself to sql injection attacks, so you need to really trust the values being supplied into this method or do some work to make sure that any value of strField does not contain actual SQL statements.
e.g. if strField contains ;[some malicious SQL here] then this will be run with the permissions of the user assigned to the connection.
#Jan has it. But as an aside you really should be disposing or closing your SqlConnection, from MSDN:
If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close or Dispose. Close and Dispose are functionally equivalent. If the connection pooling value Pooling is set to true or yes, the underlying connection is returned back to the connection pool. On the other hand, if Pooling is set to false or no, the underlying connection to the server is actually closed.
The using construct is present in C# for just such a thing:
using (SqlConnection linkToDB = new SqlConnection(strConnect)
{
// use the linkToDb here
}
Related
I am working on a school project and am having trouble converting a piece of data from a Access database into a string that I can pass to a second form in C#. I know the connection to the database is working and that I am referencing the right table in it to get the information, so I'm not sure what I'm doing wrong. It doesn't show any errors in the code, but every time I run the application, it crashes because it can't find a value from the database for the string at the string accountnumber = reader["Account_Number"].ToString(); line. Is there something I'm doing wrong?
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "select * from User_Info where Username='" +txt_Username.Text+ "' and Password='" +txt_Password.Text+ "'";
OleDbDataReader reader = command.ExecuteReader();
int count = 0;
string accountnumber = reader["Account_Number"].ToString();
while (reader.Read())
{
count = count+1;
}
if (count == 1)
{
MessageBox.Show("Login Successful!", "Success!");
connection.Close();
connection.Dispose();
this.Hide();
User_Account_Screen UAS = new User_Account_Screen();
UAS.Number = accountnumber;
UAS.ShowDialog();
Try not to reuse connections unless you have to. The main reason is that you generally want to close connections as soon as possible and it will guard you against possible race conditions later if you have multiple events/actions that can occur at the same time that are data driven.
Close your connections as soon as possible so you do not have open external resources.
To ensure that 1 and 2 occur wrap your IDisposable types in a using block.
Always use parameters instead of string concatenation in your queries. It guards against sql injection (not applicable to MS Access) and ensures you never has issues with strings that contain escape charaters.
A note about MS Access and parameters: Place holders are usually specified by the ? character and are position dependent. This is critical, you cannot rely on the name of the parameter. If you have a parameter collection with 3 parameters in that collection then those parameters must appear in the same order in the query.
I notice you probably have password as plain text, never store passwords in plain text! Instead store a hash of the password. To see if the password matches the user's supplied password at login hash that input as well and then compare that hash to the stored hash to see if they are the same. Use a secure password hashing algorithm like pbkdf2, scrypt, or bcrypt.
To see if a row exists or to just return a single value you can also use ExecuteScalar with a null check as it will return null if no records are returned. I altered the code to just return accountnumber using ExecuteScalar. I also enclosed the column name in brackets which is good practice when including characters outside the range of a-z and 0-9 in your column name.
If you were to want to return data and read it using a data reader then do not use * for your return. Specify your column names instead. This will guard your code against schema changes like columns being added or column order changes.
Here is the updated code.
string accountnumber = null;
using(OleDbConnection connection = new OleDbConnection(/*add your connection string here*/))
using(OleDbCommand command = new OleDbCommand("select [Account_Number] from User_Info where Username = ? AND Password = ?", connection))
{
command.Parameters.Add(new OleDbParameter("#username", OleDbType.VarChar)).Value = txt_Username.Text;
command.Parameters.Add(new OleDbParameter("#password", OleDbType.VarChar)).Value = txt_Password.Text;
connection.Open();
accountnumber = command.ExecuteScalar() as string;
}
if (accountnumber != null)
{
MessageBox.Show("Login Successful!", "Success!");
this.Hide();
User_Account_Screen UAS = new User_Account_Screen();
UAS.Number = accountnumber;
UAS.ShowDialog();
}
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 with executing a sql command to the DB. The command should add a new user to the 'users' table.
But when I run the code, I get this Exception on:
command.ExecuteNonQuery();
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.OleDb.OleDbException: Syntax error in INSERT INTO statement.
this is the code of the page - GetSignIn.cshtml :
#{
string Uname = Request["name"];
string userName = Request["userName"];
string pass = Request["passWord"];
string pic = Request["pic"];
string privacy = Request["privacy"];
if(pic == null)
{
pic = "Shared/defaultPic.jpg";
}
System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection();
connection.ConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Etay\Documents\Visual Studio 2012\WebSites\Josef\Shared\users.mdb";
try
{
System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand();
command.Connection = connection;
connection.Open();
command.CommandText = "INSERT INTO users (userName,passWord,Uname,pic) VALUES ('" + userName + "', '" + pass + "', '" + Uname + "', '" + pass + "', " + pic + ")";
command.ExecuteNonQuery();
Response.Redirect("../HtmlPage.html");
}
finally
{
connection.Close();
}
}
What should I change in my code? Why is it happening? Where is the syntax error in the INSERT INTO?
Use parameterized queries. Here is your statement rewritten to make use of them.
I replaced your try/finally with a using block although your try/finally was acceptable.
Parameterized queries prevent errors and Sql Injection Attacks. An error could occur in your existing code if I were to submit a tick as a part of my user name or password. In the current form this would result in an exception. This is because the tick character is used to quote strings in sql syntax.
using (System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection())
{
connection.ConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Etay\Documents\Visual Studio 2012\WebSites\Josef\Shared\users.mdb";
using (System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand())
{
command.Connection = connection;
command.CommandText = "INSERT INTO users (userName,passWord,Uname,pic) VALUES (?,?,?,?)";
command.Parameters.Add(userName);
command.Parameters.Add(pass);
command.Parameters.Add(Uname);
command.Parameters.Add(pic);
connection.Open();
command.ExecuteNonQuery();
}
}
About parameters for an OleDb connection from OleDbCommand.Parameters
Remarks
The OLE DB .NET Provider does not support named parameters for passing parameters to an SQL statement or a stored procedure called by an OleDbCommand when CommandType is set to Text. In this case, the question mark (?) placeholder must be used. For example:
SELECT * FROM Customers WHERE CustomerID = ?
Therefore, the order in which OleDbParameter objects are added to the OleDbParameterCollection must directly correspond to the position of the question mark placeholder for the parameter in the command text.
What should I change in my code?
Change to parameters (that also fixes the problem that you don;t have quotes around the pic value)
Remove the second instance of pass in your values
command.CommandText = "INSERT INTO users (userName,passWord,Uname,pic) VALUES (#userName, #pass, #Uname, #pic)";
command.Parameters.Add("#userName").Value = userName;
.. etc.
It's unclear what the type if pic is - you are passing a string but I can;t tell of the column stores a file path or if you are indending to serialize the file and store it in a pinary field.
You set 4 fields after the "INTO" clause, however you're passing 5 parameters:
"INSERT INTO users (userName,passWord,Uname,pic) VALUES ('" + userName + "', '" + pass + "', '" + Uname + "', '" + pass + "', " + pic + ")";
Just add the fifth field, or remove one parameter from the VALUES part
Please check take a look at your Insert statement, it looks like that you provided password value twice.
The number of query values and the destination fields should be same in an INSERT statement.
You have the wrong number parameters in your insert statement. For clarity, why not use string.Format to keep everything uniform? (Assuming these are all string types)
var rawSql = #"Insert INTO Users (userName,passWord,Uname,pic) VALUES ('{0}','{1}','{2}','{3}')";
command.CommandText = string.Format(rawSql, userName, pass, Uname, pic);
command.ExecuteNonQuery();
However, it also looks like you probably want to include that 5th parameter as well - just extend the format :
var rawSql = #"Insert INTO Users (userName,passWord,Uname,pic, privacy) VALUES ('{0}','{1}','{2}','{3}','{4}')";
command.CommandText = string.Format(rawSql, userName, pass, Uname, pic, privacy);
command.ExecuteNonQuery();
Since most of the answers failed to address the SQL Injection vulnerability, here's an example with parameterized queries. In addition to preventing SQL Injection attacks, it also makes it easier to troubleshoot these types of issues, and you don't need to worry about quoting or not quoting parameters.
System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection();
connection.ConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\Etay\Documents\Visual Studio 2012\WebSites\Josef\Shared\users.mdb";
try
{
System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand();
command.Connection = connection;
connection.Open();
command.CommandText = "INSERT INTO users (userName, passWord, Uname, pic, privacy) VALUES (?, ?, ?, ?, ?)";
command.Parameters.Add(userName);
command.Parameters.Add(pass);
command.Parameters.Add(name);
command.Parameters.Add(pic);
command.Parameters.Add(privacy);
command.ExecuteNonQuery();
Response.Redirect("../HtmlPage.html");
}
finally
{
connection.Close();
}
Tnx 4 the help
It happend to be a problem with the database - you can not apply a INSERT INTO statement where the column name is "password". "password" is a Reserved word
in SQL.
Tnx again,
Etay
I get an error when I try to insert a double value into a table in MySQL.
I use C# to build my transactions, and ODBC drivers to connect my database to my project.
In my aspx page I make a form which permit to the user to fill severals fields and submit the values. By an Ajax call, I go in my code behind sending the form's values. And i begin my transaction.
If the user puts for instance "15.00" in the form for the double
values, the resquest is Ok and there is not problems.
But if he puts "15.65" I get an error which says that the column
count of my table doesn't match with the request column count.
However, I check the double value and it is correct. I also try to put simple quotes next to the double value, but it doesn't work.
Here is the error :
`{"Message":"[MySQL][ODBC 5.1 Driver][mysqld-5.1.73-community]Column count doesn\u0027t match value count
at row 1","StackTrace":" à ADODB.ConnectionClass.Execute(String CommandText, Object& RecordsAffected
, Int32 Options)\r\n...
Here is my resquest :
Utils.ocn.Execute("INSERT INTO datas VALUES(0,null,null,null,null,null," + RecepAccId + ",1," + CafId + ",123,null,null," + NbrKilos + ",'" + ConvertedDate + "','" + Chrono + "','" + ConvertedTimeStamp + "','" + ChronoNum + "')", out x, -1);
The severals variables provening my function's parameters
There are 17 fields in my table and I send normally 17 values in my resquests. But when I set the double with numbers after the comma, I get a bug. And according to the error message, we can suppose that, the numbers after the comma are considerated like a other field in the insert !
In doubt I put the 17 fields'names after the table name in my resquest, but I get the same result.
Have you an idea to fix it ?
To get your database driver to treat the values from the user input as individual pieces of data, you should use a parametrised query. So if your table had a single double column called, height for example.
INSERT INTO datas height
VALUES #height
Where #height is a double value parsed from the input from the request eg using double parsedHeight = double.Parse(subbmittedValue). You can then add the parameter when making your SQL command by adding:
command.Parameters.AddWithValue("#height", parsedHeight);
As #Marko Juvančič says, using string concatenation from user input to create a sql statement is dangerous. In fact, it is a major security issue leaving you open to SQL injection. Imagine a malicious user entered a SQL query to delete datas in the form. Parametrising your query as described protects you from this by preventing the user from interfering with your query.
So putting all this together, instead of executing your concatenated statement with Utils.ocn.Execute use something like:
double parsedValue = double.Parse(valueFromRequest);
//be careful, this will throw if the user didn't enter a valid double.
string connectionString = yourConnectionString;
using (OdbcConnection connection = new OdbcConnection(connectionString))
{
connection.Open();
using (OdbcCommand command = new OdbcCommand(
"INSERT INTO datas columnName VALUES(#value)", connection))
{
command.Parameters.AddWithValue("#value", parsedValue);
command.ExecuteNonQuery();
}
}
private double failingGrade;
public double getFailingGrade()
{
using (MySqlConnection conn = new MySqlConnection(DBConnection.Connection()))
{
conn.Open();
using (MySqlCommand comm = conn.CreateCommand())
{
comm.CommandText = "select failing from gradespercentage_tab";
return failingGrade = double.Parse(comm.ExecuteScalar().ToString());
}
}
}
i want to insert data into sql server Compact edition the database table screenshot is Here >>>
i Want to add data in users the addition script is as follows
SqlCeConnection Con = new SqlCeConnection();
Con.ConnectionString = "Data Source = 'Database.sdf';" +
"Password='Password';";
Con.Open();
int Amount=Convert.ToInt32(AmBox.Text),
Code=Convert.ToInt32(MCode.Text),
Num=Convert.ToInt32(MNum.Text);
string Name=Convert.ToString(NBox.Text),
FName=Convert.ToString(SOBox.Text),
Address=Convert.ToString(AdBox.Text);
SqlCeCommand Query =new SqlCeCommand("INSERT INTO Users VALUES " +
"(++ID,Name,FName,Address,Code,Num,Amount)",Con);
Query.ExecuteReader();
When it runs it generates an error SAYING "The column name is not valid [Node Name (if any) =,Column name=ID ]
I don't figure out the problem kindly tell me thanks!
You should change your code to something like this
using(SqlCeConnection Con = new SqlCeConnection("Data Source = 'Database.sdf';" +
"Password='Password';")
{
Con.Open();
SqlCeCommand Query = new SqlCeCommand("INSERT INTO Users " +
"(Name,FName,Address,MCode,MNum,Amount) " +
"VALUES (#Name,#FName,#Address,#Code,#Num,#Amount)",Con);
Query.Parameters.AddWithValue("#Name", NBox.Text);
Query.Parameters.AddWithValue("#FName", SOBox.Text));
Query.Parameters.AddWithValue("#Address",AdBox.Text));
Query.Parameters.AddWithValue("#Code", Convert.ToInt32(MCode.Text));
Query.Parameters.AddWithValue("#Num", Convert.ToInt32(MNum.Text));
Query.Parameters.AddWithValue("#Amount" , Convert.ToInt32(AmBox.Text));
Query.ExecuteNonQuery();
}
The using statement guarantees the correct disposing of the
connection
The Parameter collection avoid Sql Injection Attacks and quoting
problems
Use of ExecuteNonQuery because this is an insert query.
Removed the ++ID, it is not a valid value to pass to the database
If the ID field is an Identity column, then you don't pass any value from code, but let the database calculate the next value.
Also, I'm not sure you really need the single quote in your connection string around the data source and password keys.
EDIT ---
Sometimes the .SDF database could be located in a different folder.
(Modern operating systems prevent writing in the application folder).
In this case is necessary to set the path to the SDF file in the connection string.
For example, the SDF could be located in a subfolder of the C:\ProgramData directory.
string conString = "Data Source=" +
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"MyAppData\\database.sdf") + ";Password=yourPassword;";