I have not much experience with SQL, Access nor C# but I find no solution to a problem that should look quite simple for someone who has more expertise.
Basically, the user fill some textboxes in a Winform and he might insert some "special" characters (at least, special for DB strings) such as '. These data are hence transferred into the database through an OleDb connection; let's say that string myString = this.textBox1.Text is the value that I would like to insert into the field MY_FIELD of the table myTable.
Starting code
My starting code was straight-forward:
OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + this.DBstring);
OleDbCommand comm = new OleDbCommand();
comm.CommandText = "INSERT INTO myTable (MY_FIELD) VALUES ('" + myString + "')";
comm.CommandType = CommandType.Text;
comm.Connection = conn;
conn.Open();
comm.ExecuteNonQuery();
conn.Close();
The above code will easily fail in the case where myString is something like guns'n'roses, because the comm.CommandText will be the following string value which is not valid SQL: INSERT INTO myTable(MY_FIELD) VALUES ('guns'n'roses').
Further research
I wasn't obviously the first newbie having this kind of problem. So I searched a bit through Stack Overflow and found the following thread where a guy had an issue inserting brackets into the command string. Hence, I've tried to adapt my code as for the accepted answer:
comm.CommandText = "INSERT INTO myTable (MY_FIELD) VALUES (?)";
comm.Parameters.Add(myString);
but this raises an InvalidCastException saying that The OleDbParameterCollection only accepts non-null OleDbParameter type objects, not String objects.
Could anyone please lead me to what's the best practice to insert any kind of string into the Access database without failing the SQL command due to characters that have a "special" meaning to the SQL interpreter?
You are correct in using OleDbParameter for this. Every time you want to pass values to your database engine you should use parameters. The only problem with your second attempt is the fact that you don't use the correct syntax to create and add a parameter to the command collection
comm.CommandText = "INSERT INTO myTable (MY_FIELD) VALUES (?)";
comm.Parameters.Add("#name", OleDbType.VarWChar).Value = myString;
This of course if your MY_FIELD is a text field, if it is numeric then you need to use the appropriate OleDbType enum.
Said that I would suggest to change your code to this example
string cmdText = "INSERT INTO myTable (MY_FIELD) VALUES (?)";
using(OleDbConnection conn = new OleDbConnection(....))
using(OleDbCommand comm = new OleDbCommand(cmdText, conn))
{
conn.Open();
comm.Parameters.Add("#name", OleDbType.VarWChar).Value = myString;
comm.ExecuteNonQuery();
}
The main difference is the Using Statement. With this syntax your disposable objects (connection and command) are correctly closed and disposed after you have finished to use them releasing any system resource used. And this happens also in case of Exceptions
The .Add method of an OleDbParameterCollection has several overloads, and the ones with a single argument expect that argument to be an OleDbParameter object.
If you want to pass the string value of a parameter then you'll need to use the overload that accepts the Name, Type, and Column Width of the parameter, i.e., this one, and then assign the .Value, like so:
comm.Parameters.Add("?", OleDbType.VarWChar, 255).Value = myString;
And, as #Steve said, you should always use parameters instead of "dynamic SQL" (which is what your first attempt was doing by "gluing" the value into the SQL statement itself).
Related
how can i do this?
is it possible to insert only on two columns when i have more than two columns in sql table please can some one help me to achieve this.
i tried this code below:
SqlConnection Conn = new SqlConnection(shoolmanangmentconn);
Conn.Open();
SqlCommand cmd = new SqlCommand("INSERT INTO tbl_TestingThatSubjects(INDO, LANGUAGE,BIOLOGY,
GEOGRAPHY) VALUES (#IDNO,#LANGUAGE,#BIOLOGY,#GEOGRAPHY)", Conn);
cmd.Parameters.AddWithValue("#IDNO", txtids.Text);
cmd.Parameters.AddWithValue("#LANGUAGE", txtlanguage.Text);
cmd.ExecuteNonQuery();
i am trying to insert into only at column LANGUAGE without inserting into other columns
please can someone teach me how to do the right thing. (am using wpf c#)
The columns either need to Allow Nulls or have a Default Value:
This is how you can extend database schema's without breaking existing codebases.
REF: https://blog.sqlauthority.com/2017/07/02/default-value-nullable-column-works-interview-question-week-129/
ps look into the using statement for unmanaged objects, such as the SqlConnection and SqlCommand objects.
You could pass through DBNull.Value for those parameters.
But if you have a default on those columns which you would like to use, then don't have them in the INSERT at all:
INSERT INTO tbl_TestingThatSubjects(IDNO, LANGUAGE)
VALUES (#IDNO, #LANGUAGE);
By the way, your code has other issues:
Dispose your connection and command objects with using blocks
Avoid AddWithValue, as it's not great at guessing the correct datatype and size to pass through.
It's often neater to keep the SQL in a separate variable. Always declare it const, it stops you getting into the habit of interpolating/concatenating dynamic SQL (with the risk of injection).
So your code should look something like this:
const string query = #"
INSERT INTO tbl_TestingThatSubjects (INDO, LANGUAGE)
VALUES (#IDNO, #LANGUAGE);
";
using(SqlConnection Conn = new SqlConnection(shoolmanangmentconn))
using(SqlCommand cmd = new SqlCommand(query, Conn))
{
cmd.Parameters.Add("#IDNO", SqlDbType.VarChar, 100).Value = txtids.Text;
cmd.Parameters.Add("#LANGUAGE", SqlDbType.VarChar, 100).Value = txtlanguage.Text;
Conn.Open();
cmd.ExecuteNonQuery();
}
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.
`ql = "select ID from Users where Username = '" + txtusername.Text + "';";
cmd = new SqlCommand(sql, Sel_Menu.con);
Sel_Menu.con.Open();
IDD = int.Parse(cmd.ExecuteScalar().ToString()); //here I get int32
Sel_Menu.con.Close();
IDD = 15;
sql = "insert into Action_Log ([ID_User],[Action_NR],[AtWhatTime]) values (#iDUser,#action_NR,getdate())";
cmd = new SqlCommand(sql, Sel_Menu.con);
cmd.Parameters.AddWithValue("#iDUser", IDD+1-1);
cmd.Parameters.AddWithValue("#action_NR", 1);
cmd = new SqlCommand(sql, Sel_Menu.con);
Sel_Menu.con.Open();
cmd.ExecuteNonQuery(); //done also with cmd.ExecuteScalar(); ...
Sel_Menu.con.Close();`
How can I fix this?
still I have "Must be declared scalar value "#iD_User"" error - everything I do - does not change this error - even not to any other error.
Just move the line that reinitialize the SqlCommand before the declarations of the parameters
cmd = new SqlCommand(sql, Sel_Menu.con);
cmd.Parameters.AddWithValue("#iD_User",IDD);
cmd.Parameters.AddWithValue("#action_NR", 1);
cmd.Parameters.AddWithValue("#atWhatTime","getdate()");
You code is adding the parameters to the previous instance of cmd not to the actually executed command. Notice also that the method SqlParameterCollection.Add(string, SqlDbType, int) means, add a parameter with name, type and SIZE. But it doesn't set the value for the parameter.
There is another error. The getDate() function is a T-SQL function. As you are writing it you are passing the string "getDate()" to your last parameter. Move it directly in the Sql command text and remove the third parameter.
sql = #"insert into Action_Log (ID_User,Action_NR,AtWhatTime) values
(#iD_User,#action_NR,getDate())";
Last but not least. In this query you use a parameterized approach (good), while the first one use a string concatenation (bad). Use always parameters to avoid Sql Injection and parsing problems.
You need to initialize your cmd before you try to add your parameters. Move this line before your parameter lines.
cmd = new SqlCommand(sql, Sel_Menu.con);
And SqlParameterCollection.Add(String, SqlDbType, Int32) overload takes size as a third parameter not value. You might need to use other overloads.
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
By the way, use using statement to dispose your database connections and commands.
Your statement cmd = new SqlCommand(sql, Sel_Menu.con); should come before adding parameters to the command.
Since you are using a single object cmd to execute both commands, it is adding parameters to previous reference and later when you initialize it again using new SqlCommand(sql, Sel_Menu.con) your parameters are lost.
sql = "insert into Action_Log (ID_User,Action_NR,AtWhatTime) values (#iD_User,#action_NR,#atWhatTime)";
cmd = new SqlCommand(sql, Sel_Menu.con);
cmd.Parameters.AddWithValue("#iD_User",SqlDbType.Int, IDD); //I had also tried: "cmd.Parameters.AddWithValue("#iD_User", IDD)"
cmd.Parameters.AddWithValue("#action_NR", 1);
cmd.Parameters.AddWithValue("#atWhatTime",DateTime.Now);
// Either pass `getdate` in your string query or send `DateTime.Now` as parameter.
//Note that DateTime.Now could result in a different value than getdate.
//Thanks to #Steve answer
Consider using parameters with your first command as well, otherwise your code is prone to SQL Injection. Also consider enclosing your command and connection object in using statement, that will ensure the disposal of resources.
private void PopuniListu(){
listBox1.Items.Clear();
conn = new OleDbConnection(connString);
conn.Open();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT Imeiprezimeautora FROM Tabela1";
dr = cmd.ExecuteReader();
while (dr.Read())
{
listBox1.Items.Add(dr.GetValue(0));
}
dr.Close();
conn.Close();
}
Here is error:
No value given for one or more required parameters.
dr = cmd.ExecuteReader()
When using an OleDb provider (with MS-Access in particular, but I can't exclude the same for other providers) the parsing engine looks if every part of the column list in the SELECT clause is present in the table queried. If, for any reason (usually a typo in the column name), the engine cannot find the corresponding column it treats the name as a parameter and expects that a parameter is supplied in the OleDbCommand.Parameters collection.
If no parameters is present then the above error is thrown.
Another possibility is the fact that you don't initialize the cmd variable in the code above.
This implies that you are using a global variable for the OleDbCommand.
A variable that may have been used for another query and its parameter collection is not empty.
Try to add this line before executing the query
cmd.Parameters.Clear();
cmd.CommandText = "SELECT Imeiprezimeautora FROM Tabela1";
However, if this is the problem, then I really suggest you to rethink this approach and avoid global variables for this kind of work.
There is no measurable performace penalty in defining and initializing an OleDbCommand locally
I'm writing a method to insert a Student into a local SQL database that contains a table with information about Students:
public void AddStudent(string name, string teachName, string pass)
{
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\Logo.sdf";
SqlCeConnection connection = new SqlCeConnection("Data Source=" + dbfile + "; Password = 'dbpass2011!'");
connection.Open();
SqlCeTransaction transaction = connection.BeginTransaction();
SqlCeCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
command.Parameters.Add("#name", name);
command.Parameters.Add("#id", this.ID);
command.Parameters.Add("#pass", MD5Encrypt.MD5(pass));
command.Parameters.Add("#tname", teachName);
command.Prepare();
command.ExecuteNonQuery();
transaction.Commit();
connection.Dispose();
connection.Close();
}
Whenever I use this, it never inserts the data to the table when I look at the contents of the Students table in the database. Originally I had this return an int so I could see how many rows it affected, which it always returned 1, so I know it's working.
I've looked for answers to this, and the answer to similar questions was that the person asking was looking at the wrong .sdf file. I've made sure that I'm looking at the right file.
Any feedback would be much appreciated!
command.CommandText = "INSERT INTO Students VALUES ('#name', '#id', '#pass', '#tname')";
You should remove the extra single quotes - this should be:
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also I am not sure why you open a transaction for a single insert - that is also not needed.
You don't need to put single quote to parametrized query, in case of parametrized query the whole data will be parsed as required,
command.CommandText = "INSERT INTO Students VALUES (#name, #id, #pass, #tname)";
Also, its better to set parameter type, size and value explicitly as below:
SqlCeParameter param = new SqlCeParameter("#name", SqlDbType.NVarChar, 100);
param.Value = name; // name is a variable that contain the data of name field
//param.Value = 'Jhon Smith'; //Directly value also can be used
Hope this would be helpful, thanks for your time.
There is most likely an exception being raised in your code; you need to add a try/catch handler and/or debug the application to figure out exactly what is happening.
However, there are at least two issues with your code:
The prepare statement requires the data types of the parameters. From the MSDN documentation:
Before you call Prepare, specify the data type of each parameter in the statement to be prepared. For each parameter that has a variable-length data type, you must set the Size property to the maximum size needed. Prepare returns an error if these conditions are not met.
You need to close the connection before disposing it (this won't affect the insert, however).