I have an database that I need to validate a user input against. I can't figure out the syntax for the while loop I'm using to validate the input.
Expecting:
/*
User inputs "Hondas"
Console checks database if "Hondas" exists.
If it doesn't exist, prompt user again.
If it does exist, continue w/ program.
*/
Current Code:
// Open a connection to MySQL
conn = new MySqlConnection(cs);
conn.Open();
//Declare DataReader
MySqlDataReader rdr = null;
// Form SQL Statement
string stm = $"select count(*) from vehicle where make = \"{sqlInput}\"";
// Prepare SQL Statement
MySqlCommand cmd = new MySqlCommand(stm, conn);
rdr = cmd.ExecuteReader();
// Output Results
while (rdr.Read() && rdr.GetInt32(0) <= 0)
{
Console.Clear();
Console.WriteLine("This make doesn't exist in the database, try again.");
sqlInput = Console.ReadLine();
// I believe I'm supposed to have some connection info here.
}
This successfully validates however the loop doesn't work properly. It only runs one time if I have multiple Incorrect entries. I'm not terribly familiar with how the connection should look. Thanks for the help.
Related
I am trying to learn C# and I'm writing a system where you have to log in, I'm storing the data in a database and loading in with code. The data is loaded in with no errors and I can Console.WriteLine it and it's all fine, but when I run comparison on it it always fails. Here is the relevant code.
Edit: I have tried without using the $ in the string comparison and it still doesn't work
private void login_button_Click(object sender, EventArgs e)
{
// App.config stores configuration data
// System.Data.SqlClient provides classes
// for accessing a SQL Server DB
// connectionString defines the DB name, and
// other parameters for connecting to the DB
// Configurationmanager provides access to
// config data in App.config
string provider = ConfigurationManager.AppSettings["provider"];
string connectionString = ConfigurationManager.AppSettings["connectionString"];
// DbProviderFactories generates an
// instance of a DbProviderFactory
DbProviderFactory factory = DbProviderFactories.GetFactory(provider);
// The DBConnection represents the DB connection
using (DbConnection connection =
factory.CreateConnection())
{
// Check if a connection was made
if (connection == null)
{
Console.WriteLine("Connection Error");
Console.ReadLine();
return;
}
// The DB data needed to open the correct DB
connection.ConnectionString = connectionString;
// Open the DB connection
connection.Open();
// Allows you to pass queries to the DB
DbCommand command = factory.CreateCommand();
if (command == null)
{
return;
}
// Set the DB connection for commands
command.Connection = connection;
// The query you want to issue
command.CommandText = $"SELECT * FROM Users WHERE Username = '{username_input.Text}'";
// DbDataReader reads the row results
// from the query
using (DbDataReader dataReader = command.ExecuteReader())
{
dataReader.Read();
//while(dataReader.Read())
//{
if ($"{password_input.Text}" ==$"{dataReader["Password"]}")
{
MessageBox.Show("Logged in");
}
else
{
MessageBox.Show("Invalid Credentials!");
}
//}
}
}
}
}
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.
I notice you probably have password as plain text, never store passwords in plain text!
In this particular case using ExecuteScalar simplifies the logic (IMO). 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.
command.CommandText = "SELECT [Password] FROM [Users] WHERE [Username] = #userName";
// using DbCommand adds a lot more code than if you were to reference a non abstract implementation when adding parameters
var param = command.CreateParameter();
param.ParameterName = "#userName";
param.Value = username_input.Text;
param.DbType = DbType.String;
param.Size = 100;
command.Parameters.Add(param);
// compared with SqlDbCommand which would be 1 line
// command.Parameters.Add("#userName", SqlDbType.VarChar, 100).Value = username_input.Text;
var result = command.ExecuteScalar()?.ToString();
if(string.Equals(password_input.Text, result, StringComparison.Ordinal))
MessageBox.Show("Logged in");
else
MessageBox.Show("Invalid Credentials!");
Start off on the right foot with learning C# with some advice Ive seen in the comments already as well some additional advice below:
Parameterize your queries at the very minimum
The below way is Open to SQL injection
command.CommandText = $"SELECT * FROM Users WHERE Username = '{username_input.Text}'";
This instead should be written as: (Keep in mind there are shorter ways to write this but I'm being explicit since you are learning)
var usernameParam = new SqlParameter("username", SqlDbType.VarChar);
usernameParam.Value = username_input.Text;
command.Parameters.Add(usernameParam);
command.CommandText = "SELECT * FROM Users WHERE Username = #username";
Secondly, debugging is your friend. You need to add a breakpoint on the line that is failing and utilize the built in Visual Studio Watchers to look at your variables. This will tell you more information than a console.writeline() and solve more problems than you might imagine.
I have a client/server app and my server stores data in a MySQL database, currently I have made a connection and I do queries without queue or something. I don't think this is a good solution for this, because when a MySQLDataReader opens another one can't be execute at the same time and first one must be closed. I think I have two options, either make a connection by every DataReader or put my queries in a queue to execute them one by one.
I want to know which one is the best or is there any way or something to prevent errors and exception which causes this error
There is already an open DataReader associated with this Connection which must be closed first.
This is how currently I am doing queries. I first get the main connection and do queries. it my causes above error.
string query = "SELECT * FROM users WHERE username = #username";
ServerModel.Database.CheckConnection(); // Get the main connection
MySqlCommand cmd = new MySqlCommand(query, ServerModel.Database);
cmd.Parameters.AddWithValue("#username", username);
UserStatus userStatus;
using (MySqlDataReader dataReader = cmd.ExecuteReader())
{
if (dataReader.Read())
{
...
dataReader.Close();
return userStatus;
}
}
To note that this server may do thousands of queries at moment. think about a chat server.
In this case please don't use the using block, I hope below approach will work fine.
string query = "SELECT * FROM users WHERE username = #username";
ServerModel.Database.CheckConnection(); // Get the main connection
MySqlCommand cmd = new MySqlCommand(query, ServerModel.Database);
cmd.Parameters.AddWithValue("#username", username);
UserStatus userStatus;
MySqlDataReader dataReader = cmd.ExecuteReader()
if (dataReader.Read())
{
...
dataReader.Close();
return userStatus;
}
I have the following code:
static void InsertRes(string Data)
{
string query = "INSERT INTO results (gamenum,result) " + Data;
query += ";";
//open connection
if (OpenConnection() == true)
{
//create command and assign the query and connection from the constructor
MySqlCommand cmd = new MySqlCommand(query, connection);
//Execute command
cmd.ExecuteNonQuery();
//close connection
CloseConnection();
}
}
I know it's not the best practices but I generate data within my application, how can I force this function to block until the query has been successfully executed upon the mysql database?
It will do that automatically.
cmd.ExecuteNonQuery(); will return after the insertion is finished.
That is standard programming behaviour. Doing multiple task simultaniously is where it gets tricky. Doing one thing after the other is just normal. Your program will continue to the next line only when the execution if the preivious line is finished.
i have made a form in c# to add data into my datatable, it works fine, all i want is to return a message when the data is inserted, and i am having an issue with that, the code is as follows:
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = ("INSERT INTO datatable (Number_Plate,Registered_Keeper,Make,Model,Year_Of_Make,Colour,Engine_Size,Transmission,Fuel_Type) Values (#Number_Plate,#Registered_Keeper,#Make,#Model,#Year_Of_Make,#Colour,#Engine_Size,#Transmission,#Fuel_Type)");
cmd.Parameters.AddWithValue("#Number_Plate", Plate.Text);
cmd.Parameters.AddWithValue("#Registered_Keeper", Keeper.Text);
cmd.Parameters.AddWithValue("#Make", Make.Text);
cmd.Parameters.AddWithValue("#Model", Model.Text);
cmd.Parameters.AddWithValue("#Year_Of_Make", Year.Text);
cmd.Parameters.AddWithValue("#Colour", Colour.Text);
cmd.Parameters.AddWithValue("#Engine_Size", Engine.Text);
cmd.Parameters.AddWithValue("#Transmission", Transmission.Text);
cmd.Parameters.AddWithValue("#Fuel_Type", Fuel.Text);
SqlDataReader reader = cmd.ExecuteReader();
if (cmd.ExecuteNonQuery()==1)
{
button1.Visible = false;
label10.Visible = true;
}
else
{
button1.Visible = false;
label10.Text = "Data Not Added Please try Again!";
label10.Visible = true;
}
}
when i run the code, i get an issue with the If statement ane the error is:
There is already an open DataReader associated with this Command which must be closed first.
any help appreciated.
just remove this line (you don't need it)
SqlDataReader reader = cmd.ExecuteReader();
makes no sense because you are inserting a record and not fetching on the database.
snippet,
// other codes
cmd.Parameters.AddWithValue("#Engine_Size", Engine.Text);
cmd.Parameters.AddWithValue("#Transmission", Transmission.Text);
cmd.Parameters.AddWithValue("#Fuel_Type", Fuel.Text);
if (cmd.ExecuteNonQuery()==1)
{
button1.Visible = false;
// other codes
You have open Connection to Database previously. So you need to close that first to execute.
conn.close()
I'm just a beginner in C#. I'm using XAMPP server for MySQL database and Visual C# 2010. Then I have created a database named "testdb" in phpMyAdmin and a table named "login". I have inserted my username and password in the table. I'm doing a simple WinForm login where I made two text boxes for username and password and a button. I have my codes done and there's no compiler error. But I had troubled in one line. It says "Unable to connect to any of the specified MySQL hosts". I added MySql.Data to my references. I want to fetch the data in the database table when I'm going to log in. Then authorize the user or if not matched, it will prompt an error message.
Here is my code:
using MySql.Data.MySqlClient;
public bool Login(string username, string password)
{
MySqlConnection con = new MySqlConnection("host=localhost;username…");
MySqlCommand cmd = new MySqlCommand("SELECT * FROM login WHERE username='" +
username + "' AND password='" + password + "';");
cmd.Connection = con;
con.Open(); // This is the line producing the error.
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.Read() != false)
{
if (reader.IsDBNull(0) == true)
{
cmd.Connection.Close();
reader.Dispose();
cmd.Dispose();
return false;
}
else
{
cmd.Connection.Close();
reader.Dispose();
cmd.Dispose();
return true;
}
}
else
{
return false;
}
}
*I hope for your your feedback. :)
Your immediate problem is probably either an incorrect connection string or the database server is not available. The connection string should be something like this
Server=localhost;Database=testdb;Uid=<username>;Pwd=<password>;
with <username> and <password> replaced with your actual values.
Besides that your code has several issues and you should definitely look into them if this is intended to become production code and probably even if this is just a toy project to learn something. The list is in particular order and may not be comprehensive.
Do not hard code your connection string. Instead move it to a configuration file.
Do not include plain text passwords in configuration files or source code. There are various solutions like windows authentication, certificates or passwords protected by the Windows Data Protection API.
Do not just dispose IDisposable instances by calling IDisposable.Dispose(). Instead use the using statement to release resources even in the case of exceptions.
Do not build SQL statements using string manipulation techniques. Instead use SqlParameter to prevent SQL injection attacks.
Do not store plain text passwords in a database. Instead at least store salted hashes of the passwords and use a slow hash function, not MD5 or a member of the SHA family.
You can use IDbCommand.ExecuteScalar to retrieve a scalar result and avoid using a data reader.
Comparing a boolean value with true or false is redundant and just adds noise to your code. Instead of if (reader.IsDBNull(0) == true) you can just use if (reader.IsDBNull(0)). The same holds for if (reader.Read() != false) what is equivalent to if (reader.Read() == true) and therefore also if (reader.Read()).
Using an O/R mapper like the Entity Framework is usually preferred over interacting with the database on the level of SQL commands.
Try modifying your ConnectionString accordingly to the Standard MySQL ConnectionString:
Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;
Source:
MySQL ConnectionStrings
You can also take a look at the following link, that shows how to connect to a MySQL database using C#:
Creating a Connector/Net Connection String (MYSQL)
Make it simple and sql injection free, and also don't forget to add MySql.Web
in your references since your using XAMPP
public bool Login(string username, string password)
{
DataTable dt = new DataTable();
string config = "server=....";
using (var con = new MySqlConnection { ConnectionString = config })
{
using (var command = new MySqlCommand { Connection = con })
{
con.Open();
command.CommandText = #"SELECT * FROM login WHERE username=#username AND password=#password";
command.Parameters.AddWithValue("#username", username);
command.Parameters.AddWithValue("#password", password);
dt.Load(command.ExecuteReader());
if (dt.Rows.Count > 0)
return true;
else
return false;
} // Close and Dispose command
} // Close and Dispose connection
}