I am trying to get a simple SQLite database working. I'm using the official SQLite extension for C# and I'm using DataGrip from IntelliJ to verify the data is there, yet my C# program doesn't get any results.
This is the code that executes the query:
SQLiteConnection connection = new SQLiteConnection(DbDsn);
User user = new User();
using (connection)
{
connection.Open();
string sql = "SELECT * FROM user WHERE username = #username ;";
SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Prepare();
command.Parameters.AddWithValue("#username", username);
SQLiteDataReader reader = command.ExecuteReader();
if (reader.Read())
{
user.Id = (int) reader["id"];
user.Username = reader["username"] as string;
user.Password = reader["password"] as string;
user.Name = reader["name"] as string;
user.LastName = reader["last_name"] as string;
user.Type = (UserTypes) reader["type"];
}
else
{
throw new ObjectNotFoundException();
}
connection.Close();
}
And this is the result of a simple Select * From user; query on the user table (done on DataGrip):
id username passw… name last_name type
1 managertest oAWpW… BENJAMIN ARIEL NAVA MARTINEZ 1
2 clerktest iRYMz… EMPLEADO PRUEBA 0
As you can see, the records are there (an I've verified that the query is being performed on the exact same file), however, the C# program seems to skip the if statement (because read returns false) as if there were no rows in the database, what is the problem here?
Call SQLiteCommand.Prepare AFTER you have completed constructing your command
//...
string sql = "SELECT * FROM user WHERE username = #username ;";
SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("#username", username);
// Call Prepare after setting the Commandtext and Parameters.
command.Prepare();
SQLiteDataReader reader = command.ExecuteReader();
//...
Related
var conn = new SqlConnection("");
try
{
conn.Open();
string s = $"SELECT Email FROM {tableName} WHERE {query}";
SqlCommand cmd = new SqlCommand(s, conn);
SqlDataReader reader = cmd.ExecuteReader();
int email = reader.GetOrdinal("Email");
while (reader.Read())
{
var response = new User
{
Email = reader.IsDBNull(email) ? null : reader.GetString(email)
};
emailList.Add(response);
}
reader.Close();
}
finally
{
conn.Close();
}
How do I update this code to verify if a table with name {tableName} exists in sql database before executing this code.
Run the following query and substitute {tablename} with the name of the table, you are looking for:
SELECT name, SCHEMA_NAME(schema_id) AS [schema], create_date, object_id, modify_date FROM sys.tables WHERE name = {tablename}
I have a code that should test a login.
When I execute literally, it works, returning one row (that's expected). When I use parameters on sqlcommand, I don't get any row.
It works (literal values for username and password):
string strConn = 'string connection';
SqlConnection conn = new SqlConnection(strConn);
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Parameters.Clear();
sqlCommand.CommandText = #"select *
from
Usuario
where
Username = 'test' and
Password = CONVERT(VARCHAR(32), ashBytes('MD5', 'test'), 2)";
conn.Open();
SqlDataReader ret = sqlCommand.ExecuteReader();
But it doesn't work (parameters values for username and password):
string strConn = 'string connection';
SqlConnection conn = new SqlConnection(strConn);
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Parameters.Clear();
sqlCommand.CommandText = #"select *
from
Usuario
where
Username = #login and
Password = CONVERT(VARCHAR(32), ashBytes('MD5', #pass), 2)";
SqlParameter user = new SqlParameter("#login", SqlDbType.NVarChar, 50) { Value = "test" };
SqlParameter pass = new SqlParameter("#pass", SqlDbType.NVarChar, 50) { Value = "test" };
List<SqlParameter> list = new List<SqlParameter>();
list.Add(user);
list.Add(pass);
sqlCommand.Parameters.AddRange(list.ToArray<SqlParameter>());
conn.Open();
SqlDataReader ret = sqlCommand.ExecuteReader();
I don't have an sintax error or something like that. The second code just don't returns rows.
I've tried to use sqlCommand.Parameters.AddWithValue, but I have no success too.
'test' and N'test' are not the same thing when you convert them to a hash. One is ASCII and the other is Unicode. If they are both ASCII then use SqlDbType.VarChar (not SqlDbType.NVarChar) in your parameter.
Difference illustrated in Sql
DECLARE #passUnicode Nvarchar(100) = N'test'
DECLARE #passAscii varchar(100) = 'test'
SELECT CONVERT(VARCHAR(32), HashBytes('MD5', #passAscii), 2) AS [Md5OfAscii]
, CONVERT(VARCHAR(32), HashBytes('MD5', #passUnicode), 2) AS [Md5OfUnicode]
Results
098F6BCD4621D373CADE4E832627B4F6, C8059E2EC7419F590E79D7F1B774BFE6
Side notes
Password Hashing
I recommend against storing passwords as MD5, MD5 is simply not secure. There are plenty of alternatives out there like pbkdf2, bcrypt, and scrypt to name a few of the more generally accepted secure password hashing algorithms.
c# structure
When working with Ado.net (or with any resources for that matter) you should wrap your Connections, DataReaders, Adapters, etc that implement IDisposable in using blocks. This will ensure external resources are always freed, even in the event of a connection.
string connectionString = "";
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand
{
CommandText = #"select * from Usuario where Username = #login and Password = CONVERT(VARCHAR(32), HASHBYTES('MD5', #pass), 2)",
CommandType = CommandType.Text,
Connection = connection
})
{
command.Parameters.Add(new SqlParameter("login", SqlDbType.VarChar, 50) { Value = "test" });
command.Parameters.Add(new SqlParameter("pass", SqlDbType.VarChar, 50) { Value = "test" });
connection.Open();
using (var dataReader = command.ExecuteReader())
{
// do some stuff
}
}
}
The problem I have is I've made a program where user may log in to check their account information (login with username and password from SQL database) however if I log in with details A then log out and Log in with details B. it will still display account information of User A but with User B log in details.
public string Username { get; set; }
//gets Username from Login form (another form)//
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=E:\Graded unit Dev\BlackMarch\BlackMarch\bin\Debug\DataBaseBM.mdf;Integrated Security=True;Connect Timeout=30");
/* (2) */
SqlCommand cmd = new SqlCommand(#"SELECT *
FROM UserData
INNER JOIN HotelData
ON (UserData.Username = HotelData.Username) ", con);
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
txtboxPass.Text = dr["password"].ToString();
txtboxFN.Text = dr["FirstName"].ToString();
txtboxSurname.Text = dr["Surname"].ToString();
txtboxAge.Text = dr["Age"].ToString();
txtboxGender.Text = dr["Gender"].ToString();
txtboxMobile.Text = dr["Mobile"].ToString();
txtboxEmail.Text = dr["Email"].ToString();
txtboxRoomType.Text = dr["RoomType"].ToString();
txtboxNoRooms.Text = dr["NoOfRooms"].ToString();
txtboxPackage.Text = dr["PackageDeal"].ToString();
txtboxGym.Text = dr["Gym"].ToString();
txtboxBeach.Text = dr["Beach"].ToString();
txtboxPool.Text = dr["SwimmingPool"].ToString();
txtboxSports.Text = dr["SportsGround"].ToString();
txtDate.Text = dr["StartDateR"].ToString();
strNoNights = dr["NoOfNights"].ToString();
strNoDays = dr["NoOfDays"].ToString();
You aren't specifying the username in your query. A join will just return all the records for all users. If you want to get data for a specific user you need to use a parameterized query that passes in the name of the current user.
This query has no WHERE condition so your code gets all data produced by the JOIN but because your while loop replaces all the data from the previous loop with the current one you end up with your controls always showing the data of the last user read
To fix you need to add a WHERE condition to your sql text in such a way that only the data of the current logged in user will be retrieved. From your code I don't know how do you store the username of the logged in user so let's assume that is stored in a variable named UserName
string cmdText = #"SELECT *
FROM UserData
INNER JOIN HotelData
ON (UserData.Username = HotelData.Username)
WHERE UserData.UserName = #user";
using(SqlConnection con = new SqlConnection(#"....."))
using(SqlCommand cmd = new SqlCommand(cmdText, con);
{
con.Open();
cmd.Parameters.Add("#user", SqlDbType.NVarChar).Value = UserName;
using(SqlDataReader dr = cmd.ExecuteReader())
{
// Now this will loop just one time, only for the logged in user
while (dr.Read())
{
....
}
}
}
I am trying to see if there is a shorter way of writing the code to run the SQL query. I was using Entity Framework before but it seems to load way slower than using SQL commands. Any suggestion would be great. Thanks in advance!
Here is the code to my SQL commands:
string query = "Select Count(*) From Employee Where Email = #Email And Password = #Password";
string queryEmployeeId = "Select EmployeeId From Employee Where Email =#Email and Password = #Password";
string queryAdmin = "Select Admin From Employee Where Email =#Email and Password = #Password";
string queryFirstName = "Select FirstName From Employee Where Email =#Email and Password = #Password";
int result = 0;
int employeeId = 0;
int admin = 0;
string employeeFirstName;
using (SqlConnection connection = new SqlConnection(#"Data Source=198.71.227.2;Initial Catalog=TaskManager;Integrated Security=False;User ID=;Password=;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False"))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("#Email", txtEmail.Text);
command.Parameters.AddWithValue("#Password", txtPassword.Text);
connection.Open();
result = (int)command.ExecuteScalar();
}
using (SqlCommand command = new SqlCommand(queryEmployeeId, connection))
{
command.Parameters.AddWithValue("#Email", txtEmail.Text);
command.Parameters.AddWithValue("#Password", txtPassword.Text);
employeeId = (int)command.ExecuteScalar();
}
using (SqlCommand command = new SqlCommand(queryAdmin, connection))
{
command.Parameters.AddWithValue("#Email", txtEmail.Text);
command.Parameters.AddWithValue("#Password", txtPassword.Text);
admin = (int)command.ExecuteScalar();
}
using (SqlCommand command = new SqlCommand(queryFirstName, connection))
{
command.Parameters.AddWithValue("#Email", txtEmail.Text);
command.Parameters.AddWithValue("#Password", txtPassword.Text);
employeeFirstName = (string)command.ExecuteScalar();
}
}
if (result > 0)
{
Session["EmployeeId"] = employeeId;
Session["Admin"] = admin;
Session["EmployeeFirstName"] = employeeFirstName;
Response.Redirect("~/MyJobSheet.aspx");
}
Originally, this was my code for the Entity Framework:
string username = txtEmail.Text;
string password = txtPassword.Text;
using (TaskManagerEntities myEntities = new TaskManagerEntities())
{
var employee = (from a in myEntities.Employees
where a.Email == username && a.Password == password
select new { a.EmployeeId, a.Admin, a.Email, a.Password, a.FirstName }).SingleOrDefault();
if (employee != null)
{
Session["EmployeeId"] = employee.EmployeeId;
Session["Admin"] = employee.Admin;
Session["EmployeeFirstName"] = employee.FirstName;
Response.Redirect("~/MyJobSheet.aspx");
}
Write a single Stored procedure which returns a table with the following columns EmployeeID, Admin, EmployeeFirstname .Also the check whether the employee exists can be done in the Stored procedure itself (Better to user IF exists instead of count(*)).
By doing this there will be only one database call instead of 4.Also as Steve mentioned make sure that the Email column is indexed
ADO.NET will always be more efficient that any ORM, because its more "low level", what you can do is turn off some features that Entity Framework provide, when you are performing read only query's. For example you case AsNoTracking() for getting your entities, but is not necessary to keep your context tracking them.
var blogs2 = context.Blogs
.Where(b => b.Name.Contains(".NET"))
.AsNoTracking()
Or you can use Dapper, to make a Repository for Read-Only query's for each entity, it uses ADO.Net approach, but is more productive to work than pure ADO.Net
I am developing a windows app using mysql and c#. In my app there's a signin page having a sign in button. When i press sign in button it do not loads the values from database and gives me the following error:
Invalid attempt to access a field before calling Read().
Visual Studio indicates the my following code as error:
connection.Open();
string query2 = "SELECT * FROM newregistration where ID='" + id+"'";
MySql.Data.MySqlClient.MySqlCommand myCommand2 = new MySql.Data.MySqlClient.MySqlCommand(query2, connection);
// First Name Reader
MySqlDataReader fnamereader = myCommand2.ExecuteReader();
fnamereader.Read();
Fname = fnamereader.GetString(fnamereader.GetOrdinal("FirstName"));
fnamereader.Close();
// Second Name Reader
MySqlDataReader snamereader = myCommand2.ExecuteReader();
snamereader.Read();
Sname = snamereader.GetString(snamereader.GetOrdinal("SecondName"));
snamereader.Close();
// EMAIL ID Reader
MySqlDataReader emailreader = myCommand2.ExecuteReader();
emailreader.Read();
EmailID = emailreader.GetString(emailreader.GetOrdinal("EmailID"));
emailreader.Close();
DataSet ds = new DataSet();
MyAdapter.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
profile windwo = new profile(this.id, this.Fname,this.Sname,this.EmailID);
AddUserProfileInformation win = new AddUserProfileInformation(this.id);
this.Hide();
windwo.Show();
}
else
{
MessageBox.Show("Sorry Wrong information entered.");
}
connection.Close();
Please help me in sorting out the problem as I am new to development.
You don't need to use a seperate MySqlDataReader every column of your sql query. Just use one MySqlDataReader and read all column values in it.
Looks like, you just need to use something like;
if(fnamereader.Read())
{
Fname = fnamereader.GetString(fnamereader.GetOrdinal("FirstName"));
Sname = fnamereader.GetString(fnamereader.GetOrdinal("SecondName"));
EmailID = fnamereader.GetString(fnamereader.GetOrdinal("EmailID"));
}
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
string query2 = "SELECT * FROM newregistration where ID=#id";
MySqlCommand myCommand2 = new MySqlCommand(query2, connection);
myCommand2.Parameters.AddWithValue("#id", id);
You don't need to keep executing the datareader before each field
MySqlDataReader reader = myCommand2.ExecuteReader();
reader.Read();
Fname = reader.GetString(reader.GetOrdinal("FirstName"));
Sname = reader.GetString(reader.GetOrdinal("SecondName"));
EmailID = reader.GetString(reader.GetOrdinal("EmailID"));
reader.Close();
A couple of other things
Please use parameterised sql for binding variables - don't just concatenate the variable into sql text - this has security and performance benefits.
You should check the boolean result of the reader.Read() before using it