I have an WPF application that reads data from a file like so:
foreach (String line in File.ReadAllLines(file, Encoding.UTF8))
{}
Each line is then parsed and displayed on the screen which all works fine. Some of the data has cyrillic alphabet in it and the strings that I'm using to store this data in are also displayed fine on the screen in the app window.
However, after that I'm using those same strings to insert them into MySQL database. I'm building a query and firing it up MySqlCommand cmd = new MySqlCommand(query, conn); which successfully inserts a new line in the database with the appropriate information. Numbers are all fine, however all the strings that go into the database and have cyrillic letters are displayed as ????????
Database engine is InnoDB and the encoding of the table and all varchar fields in it is utf_general_ci so any idea what is going on and how can I save the correct string in the database?
EDIT:
Per request, here's some code. Database connection:
conn = new MySqlConnection();
conn.ConnectionString = "//censored//";
And the file reading / db loading, shortened for the purposes of this code snippet:
foreach (String line in File.ReadAllLines(file, Encoding.UTF8))
{
string[] tokens = line.Split('|');
string query = "INSERT INTO myTable SET first_name = '" + tokens[0] + "'" + ", last_name = '" + tokens[1] + "'";
MessageBox.Show(tokens[0]);
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.ExecuteNonQuery();
}
The message box shows the name as it should be but what goes into the database is ???????.
After some headbanging I did figure out where the problem is so posting an answer for all to see:
The key part is the way you establish your connection to the database:
conn.ConnectionString = #"Server = YOURSERVER; Database = YOURDB; Uid = YOURUSER ; Pwd = 'YOURPASSWORD'; charset=utf8;";
I was missing the charset=utf8; part before so I assume all kinds of non-utf8 junk was going to the database regardless of the fact that I was encoding in UTF8 on both sides of the connection. Hope this helps!
Related
I am making a simple project in ASP.NET and have this code to proceed to login, detect the role and open the exact page.
string cmdText = "SELECT Username,Role FROM Login WHERE Username = '" + TextBox1.Text + "' AND Password = '" + TextBox2.Text + "'";
string username = "";
string role = "";
using (SqlCommand SelectCommand = new SqlCommand(cmdText, connectionstring))
{
SqlDataReader myReader;
connectionstring.Open();
myReader = SelectCommand.ExecuteReader();
while (myReader.Read())
{
username = myReader["Username"].ToString();
role = myReader["Role"].ToString();
}
myReader.Close();
Response.Redirect(role + ".aspx");
}
I set it role+".aspx" because I was having some weird error with the if function.. it wasn't working properly..
But still was having problem redirecting to the page.. and I notice this
So, confused by this error I decided to check the data in SQL Server, and there is this:
There are 5 white spaces after the role.. I tried to delete them. But after save the data the spaces apear again.. I notice that the same thing is with the name and password
but now there are 9 white spaces.. looks like SQL Server Management Studio is trying to fill the max 10 letters...
Username, password and role are nchar(10) type.. is that the problem?
Should I change to fix that? or it can be done on other way
An nchar column stores its values in a fixed length space. If you declare a column to be of type nchar(10) then your values are all padded with spaces to reach the 10 characters length.
If you can change your schema then change the column to be of nvarchar(10) type and (not sure about it) probably you need to reedit all the values already there.
See What is the difference between char, nchar, varchar, and nvarchar in SQL Server?
If you can't change the schema then you could Trim the results in code
Response.Redirect(role.Trim() + ".aspx");
However, looking at your code, I see a very big problem. You are using the famigerate string concatenation to build your query. This is a well know weakness on code and leads to Sql Injection attacks and to parsing errors.
You should fix ASAP that query using parameters
string cmdText = "SELECT Role FROM Login WHERE Username = #name AND Password = #pass";
// You already know the username
string username = textBox1.Text;
string role = "";
using (SqlCommand SelectCommand = new SqlCommand(cmdText, connectionstring))
{
connectionstring.Open();
SelectCommand.Parameters.Add("#name", SqlDbType.NVarChar).Value = textBox1.Text;
SelectCommand.Parameters.Add("#pass", SqlDbType.NVarChar).Value = textBox2.Text;
using(SqlDataReader myReader = SelectCommand.ExecuteReader())
{
while (myReader.Read())
{
role = myReader["Role"].ToString();
}
}
Response.Redirect(role.Trim() + ".aspx");
}
As a final note, consider that storing passwords in clear text in a database is another security weakness to avoid. A well know good practice is to hash and salt a password before storing it in the database
You should use nvarchar(10) instead of nchar(10). char and nchar will always have a fixed length. So if you store an 'a' in a nchar(10) or char(10) it will get padded to the right with spaces to 10 charachters. If you store it as a nvarchar(10) it will be stored as the length of your string.
Only use nchar and char when the length of the column will always be the same.
After converting from nchar to nvarchar. You should also update the data in your columns so they won't have the trailing spaces anymore. (Do this for all the columns which you wish to convert)
update Login set Role = LTRIM(RTRIM(Role))
You can use trim() in the c# code to prevent %20 (blank space) in the url, It is because the role returned by database is 'admin____' (____ = blank space) and not 'admin', There are two options,
1. string cmdText = "SELECT Username,LTrim(RTrim(Role)) FROM Login WHERE Username = '" + TextBox1.Text + "' AND Password = '" + TextBox2.Text + "'";
(or)
2. Response.Redirect(role.trim() + ".aspx");
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;";
I'm trying to create a database through C# without using SMO, is it possible?! I've got this at the moment but I'm 99% it's wrong;
connectionString2 = "SERVER=" + server + ";" + "DATABASE=master;"
+ "UID=" + uid + ";" + "PASSWORD=" + password + ";"
this.OpenConnection(); //Which opens the above connection
// The Lines array has stored the sql statements to create the databse
// and tables for that database
foreach (string line in lines)
{
create_database += line;
}
MySqlCommand cmd = new MySqlCommand(create_database, connection2);
cmd.ExecuteNonQuery();
is it actually possible do this without using SMO?!
You have to use MySqlCommand as you're doing in the example.
MySqlCommand cmd = new MySqlCommand("CREATE DATABASE YAYNEWDATABASE;", connection2);
connection2.Open();
cmd.ExecuteNonQuery();
connection2.Close();
Alternatively if you're going to be doing a lot of modification I'd suggest trying a tool like Manatee to handle your database creation/migration as part of a build script. This is making a lot of assumptions about what you're doing, but it doesn't hurt to be aware of it.
This adds migrations similar to those in rails to .net.
{
up: "CREATE TABLE Orders (ID {pk}, OrderNumber {string} NOT NULL, SubTotal {money})",
down: "DROP TABLE Orders"
}
You might use something like this:
var connection = new MySqlConnection("Data Source=server;UserId=root;PWD=pass;");
var command = new MySqlCommand("CREATE DATABASE FancyDatabase;", connection);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
Note: there is no default database, and you need to have the rights. And: this is untested code, uses standard sql features.
What I have is an extremely large text file that needs to go into a specific column at a specific raw. The file is around 100k lines. So what I want to do is read the whole file, and for each line append into that specific SQL column the line. Here's what I have but i really need help on the SQL query
string[] primaryfix = File.ReadAllLines(dinfo+"\\"+filex);
string filename = filex.ToString();
string[] spltifilename = filename.Split('.');
foreach (string primary in primaryfix)
{
string sqltable = ("dbo.amu_Textloadingarea");
string sql = "update " + sqltable + " set [Text] = [Text] + '" + primary + "' where begbates = '" + spltifilename[0] + "'";
SqlConnection con = new SqlConnection("Data Source= Corvette ;Initial Catalog= GSK_Avandia_SSECASE;Integrated Security= SSPI");
con.Open();
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader reader = cmd.ExecuteReader();
con.Close();
}
everything is fine except for the string sql, it doesn't update the way I would like it to.
Any help is always appreciated.
Look likes you're trying to read from the database with that code inside the loop. SqlDataReader provides a way to read rows from the database, but not the other way around.
Replace
SqlDataReader reader = cmd.ExecuteReader();
with
cmd.ExecuteNonQuery();
The first thing I see is that you are not escaping the input from the text file; any SQL escape characters (like a single quote) will break that command. I'd recommend using parameters so you needn't worry about escapes at all.
Other than that, nothing pops to mind that would suggest why the command isn't working, but I do wonder if it might not cause fewer problems if it's such a large file to read it line-by-line rather than all at once.
I have a bunch of 2 line (with header row) '|' delimited text files. I need to import this into a specific SQL table and I'm having a hard time with the command.
string sqltable = ("dbo.SLT_C" + "60" + "Staging");
string[] importfiles= Directory.GetFiles(#"K:\jl\load\dest", "*.txt")
SqlConnection con = new SqlConnection("Data Source=" + "Cove" + ";Initial Catalog=" + "GS_Ava_MCase"+ ";Integrated Security=" + "SSPI");
con.Open();
foreach (string importfile in importfiles)
{
}
or maybe I am going about this the whole wrong way.
You could look at a ready-made solution, like FileHelpers. This FREE library allows you to define the structure of your file by means of a class describing the fields in your file, and then you can easily load the whole file into an array of that class type.
Once that's done, just simply iterate through the objects, and save them to your SQL Server.
Or check out the SQL Bulkcopy options:
bcp command line utility
SqlBulkCopy class in ADO.NET - also see this article at SQL Team
If you want to do it in "straight" ADO.NET, use something like this approach:
string sqltable = "dbo.SLT_C60Staging";
string[] importfiles = Directory.GetFiles(#"K:\jl\load\dest", "*.txt");
// try to wrap your ADO.NET stuff into using() statements to automatically
// dispose of the SqlConnection after you're done with it
using(SqlConnection con = new SqlConnection("Data Source=Cove;Initial Catalog=GS_Ava_MCase;Integrated Security=SSPI"))
{
// define the SQL insert statement and use parameters
string sqlStatement =
"INSERT INTO dbo.YourTable(DateField, TimeField, TextField) VALUES(#Date, #Time, #Text)";
// define the SqlCommmand to do the insert - use the using() approach again
using(SqlCommand cmd = new SqlCommand(sqlStatement, con))
{
// define the parameters for the SqlCommand
cmd.Parameters.Add("#Date", SqlDbType.DateTime);
cmd.Parameters.Add("#Time", SqlDbType.DateTime);
cmd.Parameters.Add("#Text", SqlDbType.VarChar, 1000);
// loop through all files found
foreach (string importfile in importfiles)
{
// read the lines from the text file
string[] allLines = File.ReadAllLines(importfile);
con.Open();
// start counting from index = 1 --> skipping the header (index=0)
for (int index = 1; index < allLines.Length; index++)
{
// split up the data line into its parts, using "|" as separator
// items[0] = date
// items[1] = time
// items[2] = text
string[] items = allLines[index].Split(new char[] { '|' });
cmd.Parameters["#Date"].Value = items[0];
cmd.Parameters["#Time"].Value = items[1];
cmd.Parameters["#Text"].Value = items[2];
cmd.ExecuteNonQuery();
}
con.Close();
}
}
}
That should work - you're question was too vague to know exactly what data will be in the lines, and what kind of SQL insert statement you'd need...
Using the text ODBC driver might work as well. In the ODBC administrator, you can choose the "Microsoft Access Text Driver". It allows you to choose the delimiter type. After setting up the data source, import to a data table. From there, it should be fairly simple to move the data into a SQL Server table.