I have an insert query to execute from within a C# against a SQL Server database.
The column I am inserting to is of type nvarchar.
the data I am inserting to that column is non-english.
Is it sufficient for me to use AddWithValue in order to pass the non-english data to the server? like this example:
string dogName = "עברית";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("INSERT INTO Dogs1(Name) VALUES #Name", connection))
{
command.Parameters.AddWithValue("Name", dogName);
command.ExecuteNonQuery();
}
}
Or must I use the N prefix to declare it unicode? like it says so here.
If I am understanding the question correctly, you can explicitly set the SqlCommand parameter to be a specific data type. You will be able to set it to be nvarchar as shown by the following link: http://msdn.microsoft.com/en-us/library/yy6y35y8.aspx
This below code snippet is taken directly from MSDN:
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
This uses an explicitly created SqlParameter instance, but it is the same idea by indexing the SqlParameterCollection of the SqlCommand instance.
I believe the link at the bottom is only really talking about values within SQL itself.
As far as I'm aware, the code you've got should be absolutely fine - otherwise there'd be no way of specifying Unicode text.
Of course, it's probably worth validating this - but I'd be very surprised if it didn't work.
Related
I'm trying to return all rows of a single column in my database to populate a list. When I execute the stored procedure in SQL, it works fine, but nothing gets returned when I try to do it in C#.
public static List<string> GetRows(string filter_one, string filter_two)
{
var retrievedRows = new List<string>();
var storedProc = "dbo.MyStoredProc";
using (SqlConnection connection = new SqlConnection(MY_CONNECTION_STRING))
using (SqlCommand command = new SqlCommand(storedProc, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
retrievedRows.Add(reader.GetString(0));
}
}
}
return retrievedRows;
}
Any ideas? I get no errors in the console or when I run it on IIS Express either. When I watch retrievedRows, the size stays at 0 even though when I run the same query in SQL with the same passed parameters, it returns results for me.
EDIT: Please excuse me, my brain must be running a bit slow today. One of the parameters I was passing was pointed at the (empty) value of the wrong webcontrol. I don't know how I missed this.
There is only one issue with your posted snippet that I can see, which could pose a problem:
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
In this section, you're adding two VARCHAR parameters but not specifying a length for them. Try changing your code to add a length specification:
var filterOne = new SqlParameter("FilterOne", System.Data.SqlDbType.VarChar, 50);
The constructor in use here is SqlParameter(string, SqlDbType, int):
Parameters
-
parameterName (String): The name of the parameter to map.
dbType (SqlDbType): One of the SqlDbType values.
size (Int32): The length of the parameter.
When working with VARCHAR you must specify a length or anything outside of the default length (which is 1 byte for definitions and variables, and 30 bytes for CAST and CONVERT) will be truncated:
When n isn't specified in a data definition or variable declaration statement, the default length is 1. If n isn't specified when using the CAST and CONVERT functions, the default length is 30.
The following query in C# doesn't work, but I can't see the problem:
string Getquery = "select * from user_tbl where emp_id=#emp_id and birthdate=#birthdate";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
Reader.HasRows returns no result but when I query it to my database I got data.
I'll assume your code is actually not quite as presented, given that it wouldn't currently compile - you're using cmdR before you declare it.
First, you're trying to use named parameters, and according to the documentation of OdbcCommand.Parameters, that isn't supported:
When CommandType is set to Text, the .NET Framework Data Provider for ODBC does not support passing named parameters to an SQL statement or to a stored procedure called by an OdbcCommand. In either of these cases, use the question mark (?) placeholder.
Additionally, I would personally avoid using AddWithValue anyway - I would use something like:
string sql = "select * from user_tbl where emp_id = ? and birthdate = ?";
using (var connection = new OdbcConnection(...))
{
connection.Open();
using (var command = new OdbcCommand(sql, connection))
{
command.Parameters.Add("#emp_id", OdbcType.Int).Value = userValidate.EmployeeId;
command.Parameters.Add("#birthdate", OdbcType.Date).Value = userValidate.BirthDate;
using (var reader = command.ExecuteReader())
{
// Use the reader here
}
}
}
This example uses names following .NET naming conventions, and demonstrates properly disposing of resources... as well as fixing the parameter issue.
I do think it's slightly unfortunate that you have to provide a name for the parameter when adding it to the command even though you can't use it in the query, but such is life.
Use like this:
string Getquery = "select * from user_tbl where emp_id=? and birthdate=?";
cmdR.Parameters.AddWithValue("#emp_id", userValidate.emp_id);
cmdR.Parameters.AddWithValue("#birthdate", userValidate.birthdate);
OdbcCommand cmdR = new OdbcCommand(Getquery, conn);
OdbcDataReader Reader = cmdR.ExecuteReader();
while(Reader.Read())
{
//Do something;
}
I know this thread is old, but I wanted to share my solution for anyone else coming up on this.
I was having issues with the typical method that Jon posted. I have used it before, but for some reason with this new string I had it was not wanting to actually place the parameter correctly and was causing the reader to not work.
I ended up doing something like this instead, since in the end we are just replacing parts of a string.
string sql = "select * from user_tbl where emp_id = "+ var1 +" and birthdate = "+
var2""
OdbcCommand command = new OdbcCommand(sql);
This was easier for me to get to work. Be warned though, I am not sure if it has any specific drawbacks when compare to using the command parameter method.
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).
In SQL:
INSERT INTO [dbo].[tblFiles]
([FullFilePath]
,[LastModified])
VALUES
('P:\test\test.csv',
null)
This will store the full path in the database :)
However, I need to do this in code.
SqlConnection connection = new SqlConnection(DatabaseHelper.ConnectionString);
connection.Open();
SqlCommand command = new SqlCommand( "stpInsertFile", connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#filepath", System.Data.SqlDbType.VarChar));
command.Parameters["#filepath"].Value = article.FullFilePath;
command.Parameters.Add(new SqlParameter( "#LastModified", System.Data.SqlDbType.DateTime));
command.Parameters["#LastModified"].Value = article.LastModified;
int newArticleID = Convert.ToInt32((decimal)command.ExecuteNonQuery());
command.Dispose();
connection.Close();
connection.Dispose();
return newArticleID;
With this all I get is 'P' in the full path column.
So I tried using LINQ and got the same result.
public int InsertArticleUsingLINQ(tblFile article) {
DataClassesDataContext context = new DataClassesDataContext();
tblFile newFileEntry = new tblFile();
newFileEntry.FullFilePath = article.FullFilePath;
newFileEntry.LastModified = article.LastModified;
context.tblFiles.InsertOnSubmit(newFileEntry);
context.SubmitChanges();
return newFileEntry.ID;
}
I'm not doing anything with the string before passing it to the database insert functions. I read that you need to escape the backslash but it seems to be escaping on the quote. Also read that you need an # symbol before the sql but how do you add this to a parameter?
warning: since you didn't share the stored procedure code this is just a wild guess.
did you set the size of the #filePath parameter in the definition of your stored procedure?
if you declare it as:
create procedure stpInsertFile
#filepath varchar,
#LastModified datetime
as
...
then you parameter is created as varchar(1) because of the default behaviour of varchar datatype and that would produce the result you get.please check reference documentation for char and varchar datatype on ms website.
if stpInsertFile is a Stored Procedure you will have to set in your code:
...
command.CommandType = CommandType.StoredProcedure;
else you have to set the query string in your command:
SqlCommand command = new SqlCommand( "INSERT INTO [dbo].[tblFiles] ([FullFilePath] [LastModified]) VALUES (#filepath,#LastModified)", connection);
...
Of the above options, the ADO code did not copy the full path, even with manually adding quotes. All I ever got was one character in the database.
On further inspection, the parameter was of type Varchar(1) !!! I changed this to Varchar(255) an it worked.
Note, the LINQ to SQL function did insert the full path as this was not using the Stored Procedure.
Apologies for the mistake.
`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.