I'm coding a Windows Forms login page for an administration application. My problem is, that when I try to log on, I get the error message
System.InvalidOperationException: 'The connection is already open.'
Any help would be appreciated
public partial class Form1 : Form
{
MySqlConnection con = new MySqlConnection (#"Database= app2000; Data Source = localhost; User = root; Password =''");
int i;
public Form1()
{
InitializeComponent();
}
private void btnClose_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void btnLogin_Click(object sender, EventArgs e)
{
i = 0;
con.Open();
MySqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM adminlogin WHERE username='" + txtBoxUsername + "'AND password='" + txtBoxPassword + "'";
cmd.ExecuteNonQuery();
DataTable dt = new DataTable();
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
da.Fill(dt);
i = Convert.ToInt32(dt.Rows.Count.ToString());
if (i == 0)
{
lblerrorInput.Show();
}
else
{
this.Hide();
Main ss = new Main();
ss.Show();
}
}
}
Do not cache Connection, it's a typical antipattern, but recreate it when you need it
public partial class Form1 : Form {
...
//DONE: Extract method
private static bool UserExists(string userName, string password) {
//DONE: Do not cache connections, but recreate them
using (MySqlConnection con = new MySqlConnection (#"...") {
con.Open();
//DONE: wrap IDisposable into using
using (MySqlCommand cmd = con.CreateCommand()) {
cmd.CommandType = CommandType.Text;
//DONE: Make query being readable
//DONE: Make query being parametrized
cmd.CommandText =
#"SELECT *
FROM adminlogin
WHERE username = #UserName
AND password = #PassWord"; // <- A-A-A! Password as a plain text!
//TODO: the simplest, but not the best solution:
// better to create parameters explicitly
// cmd.Parameters.Add(...)
cmd.Parameters.AddWithValue("#UserName", txtBoxUsername);
cmd.Parameters.AddWithValue("#PassWord", txtBoxPassword);
// If we have at least one record, the user exists
using (var reader = cmd.ExecuteReader()) {
return (reader.Read());
}
}
}
}
Finally
private void btnLogin_Click(object sender, EventArgs e) {
if (!UserExists(txtBoxUsername.Text, txtBoxPassword.Text))
lblerrorInput.Show();
else {
Hide();
Main ss = new Main();
ss.Show();
}
}
You forgot to close the connection, use con.Close() at the end to close the connection and avoid this error the next time the event fires.
There are some mistakes in your code.
You should close the sql connection when you finished your process.
I suggest you to use using statement to dispose connection instance after complete database actions.
Also, you should use command parameters to prevent Sql injection.
You can declare connection string like this;
private string _connectionString = #"Database= app2000; Data Source = localhost; User = root; Password =''";
The method part looks like;
using (var con = new MySqlConnection(_connectionString))
{
i = 0;
con.Open();
MySqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM adminlogin WHERE username = #username and password = #password";
cmd.Parameters.AddWithValue("#username", txtBoxUsername);
cmd.Parameters.AddWithValue("#password", txtBoxPassword);
cmd.ExecuteNonQuery();
DataTable dt = new DataTable();
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
da.Fill(dt);
i = Convert.ToInt32(dt.Rows.Count.ToString());
if (i == 0)
{
lblerrorInput.Show();
}
else
{
this.Hide();
Main ss = new Main();
ss.Show();
}
con.Close();
}
First, don't cache your Connection objects. It's a terrible practice and I've had to go back and fix it every time I accept a new job and inherit code. Most database access classes implement IDisposable, so use using and take advantage of it to keep your code clean. FYI, Readers and Adapters are also IDisposable so you can do the same with them, too.
string command = "select stuff from mydata";
string connection = GetConnectionStringFromEncryptedConfigFile();
using (var conn = new SqlConnection(connection))
{
using (var cmd = new SqlCommand(command, conn))
{
cmd.Connection.Open();
//do stuff
}
}
Second, if you're forced to use a cached connection (i.e., you inherited horrible code and don't have time to fix it yet), check your State first.
if(conn.State != System.Data.ConnectionState.Open)
{
conn.Open();
}
Note that there are a lot more states than just Open and Closed, and if you try to open a connection that is busy, you'll still get errors. It's still a much wiser approach to use the IDisposable implementations with using so you don't have to worry about this sort of thing so much.
Related
I'm new to c# programming and have a problem retrieving data from database to a label text. Here is the code what I was trying to do.
private void label3_Click_1(object sender, EventArgs e)
{
MySqlConnection con = new MySqlConnection("Server=localhost; Database=car_rental; user=root; Pwd=; SslMode=none");
DataTable dTable = new DataTable();
con.Open();
MySqlDataReader dr = null;
MySqlCommand cmd = new MySqlCommand("Select * from login where username=" + username, con);
dr =cmd.ExecuteReader();
while (dr.Read())
{
label3.Text = (dr["username"].ToString());
}
con.Close();
}
The problem in your code is created by the concatenation of a string (username) to another string (the sql query). This is a well known source of problems, going from syntax errors (the engine is not able to parse correctly the query text) to a much worse problem known as Sql Injection.
The well known solution is to use parameters instead of concatenated strings
private void label3_Click_1(object sender, EventArgs e)
{
using(MySqlConnection con = new MySqlConnection("Server=localhost; Database=car_rental; user=root; Pwd=; SslMode=none"))
{
con.Open();
// A single string with a parameter placeholder
string sqlCmd = "Select * from login where username=#name";
using(MySqlCommand cmd = new MySqlCommand(sqlCmd, con))
{
// Associate a value to the required parameter
cmd.Parameters.Add("#name", MySqlDbType.VarChar).Value = username;
using(MySqlDataReader dr =cmd.ExecuteReader())
{
// Supposing you have just one user with that name
if(dr.Read())
{
label3.Text = dr["username"].ToString();
}
else
{
label3.Text = "User not found!";
}
}
}
}
Notice how I have added the using statement around each disposable object required to query the database. This statement ensures that the objects involved are disposed at the end of their use freeing the valuable unmanaged resource kept during their usage.
When I enter a RegiD to check-in, it takes me to my form 2 but it happens even if the number is not in my RegiD column in my SQL Database. How do it get it to check if the number I entered in my text box is in my SQL Database?
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-UHGLPQ8\SQLEXPRESS;Initial Catalog=Test Vol;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM MastVolData WHERE [RegiD] =' " + textBox1.Text + "'" , con);
SqlDataReader dr = cmd.ExecuteReader();
if (dr.Read() == true)
{
using (Form2 frm = new Form2(textBox1.Text))
{
frm.ShowDialog();
}
}
else
{
MessageBox.Show("Wrong I.D! Please try again!");
}
textBox1.Clear();
}
}
}
First, you need to stop concatenating strings to create SQL statements and start using parameterized queries. Otherwise you are risking SQL Injection attacks.
Second, you need to learn when to use the using statement.
Third, You need to learn about the HasRows property of the SqlDataReader.
Your code should end up looking like this:
private void button1_Click(object sender, EventArgs e)
{
// use this local variable to prevent keeping sql connection and an active data reader longer than neccessary
var hasRows = false;
// You should really get the connection string from your web.config file, I left it hard coded so that you can test.
var connectionString = #"Data Source=DESKTOP-UHGLPQ8\SQLEXPRESS;Initial Catalog=Test Vol;Integrated Security=True";
// Use the using statement for any local variable that implements the IDisposable interface
using (var con = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("SELECT * FROM MastVolData WHERE [RegiD] = #RegiD;", con))
{
cmd.Parameters.Add("#RegiD", SqlDbType.VarChar).Value = textBox1.Text;
con.Open();
using (var dr = cmd.ExecuteReader())
{
hasRows = dr.HasRows;
}
}
}
// Now that your sql connection is closed and disposed, you can use ShowDialog
if (hasRows)
{
using (Form2 frm = new Form2(textBox1.Text))
{
frm.ShowDialog();
}
}
else
{
MessageBox.Show("Wrong I.D! Please try again!");
}
textBox1.Clear();
}
}
So currently I have this:
private void button1_Click(object sender, EventArgs e) {
string username = txtboxUsername.Text;
string password = txtboxPassword.Text;
string salt = string.Empty;
connection.Open();
MySqlCommand sql = new MySqlCommand("SELECT salts FROM users WHERE usernames = #username;", connection);
sql.Parameters.AddWithValue("#username", username);
using (MySqlDataReader reader = sql.ExecuteReader()) {
if (reader.HasRows) {
reader.Read();
salt = reader.GetString(0);
} else {
MessageBox.Show(sql.CommandText);
}
}
}
Now here is the issue, I don't get a compiler error when I run this, yet the sql.Parameters.AddWithValue( .. ); part doesn't actually add the string 'username' to the sql query. It simply leaves it at #username. Does anyone know what I am doing wrong here?
You forgot to call .Prepare().
MySqlCommand sql = new MySqlCommand("SELECT salts FROM users WHERE usernames = #username;", connection);
sql.Prepare(); // You forgot this *********************
sql.Parameters.AddWithValue("#username", username);
Below is an example using the proper resources with a using block:
using (MySqlConnection lconn = new MySqlConnection(connString))
{
lconn.Open();
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.Connection = lconn;
cmd.CommandText = "update " + activeTblName + " set bn=#bn where qId=#qId";
cmd.Prepare();
cmd.Parameters.AddWithValue("#qId", pqId);
cmd.Parameters.AddWithValue("#bn", ptheValue);
cmd.ExecuteNonQuery();
}
}
Tweak yours accordingly to clean up the resources automatically for you. MSDN shows examples of all of this.
I need assistance to display sql server values in a windows forms. In the application below if the query returns a row then the values are displayed. I understand that I am to use sqldatareader but so far I have been unsuccessful how to add it.
SqlConnection ChuoDB_Connection = new SqlConnection("Data Source=test-PC\\tester;Initial Catalog=Chuo;Integrated Security=True;Pooling=False");
SqlDataAdapter select_adapt;
private void btn_guardian_student_search_Click(object sender, EventArgs e)
{
if (rd_btn_guardian_student_no.Checked == true)
{
DataSet ds = new DataSet();
SqlDataReader dr;
ChuoDB_Connection.Open();
select_adapt = new SqlDataAdapter("SELECT * FROM Guardian WHERE STUDENT_NO = #student_no", ChuoDB_Connection);
select_adapt.SelectCommand.Parameters.Add("#student_no", SqlDbType.Int).Value = Convert.ToInt32(txt_bx_guardian_student_search.Text);
select_adapt.Fill(ds);
if (ds.Tables[0].Rows.Count == 0)
{
lbl_guardian_student_search.Text = "No Guardian record exists for this student. Please enter the Guardian Information";
ChuoDB_Connection.Close();
}
if (ds.Tables[0].Rows.Count > 0)
{
lbl_guardian_student_search.Text = "";
while (dr.read())
{
txtBox1.Text = rdr.Item["DBFieldName1"].ToString();
txtBox2.Text = rdr.Item["DBFieldName2"].ToString();
}
}
}
}
I think you do not need DataAdapter and DataSet here.
Try just with the DataReader:
string _connectionString = "Data Source=test-PC\\tester;Initial Catalog=Chuo;Integrated Security=True;Pooling=False";
string _selectCommand = #"SELECT * FROM Guardian WHERE STUDENT_NO = #student_no";
here code for the click handler:
SqlParameter parameter = new SqlParameter("#student_no", SqlDbType.Int);
parameter.Value = Convert.ToInt32(txt_bx_guardian_student_search.Text);
using (IDbConnection connection = new SqlConnection(_connectionString))
{
connection.Open();
using (IDbCommand command = connection.CreateCommand())
{
command.Connection = connection;
command.CommandText = _selectCommand;
command.Parameters.Add("#student_no", SqlDbType.Int).Value = Convert.ToInt32(txt_bx_guardian_student_search.Text);
IDataReader reader = command.ExecuteReader();
if (reader.Read())
{
txtBox1.Text = reader["DBFieldName1"].ToString();
txtBox2.Text = reader["DBFieldName2"].ToString();
}
else
{
lbl_guardian_student_search.Text = "No Guardian record exists for this student. Please enter the Guardian Information";
}
}
}
To use a SqlDataReader you need to initialize it using the ExecuteReader method of a SqlCommand. So in your code you could discard all the part relative to the SqlDataAdapter
private void btn_guardian_student_search_Click(object sender, EventArgs e)
{
if (rd_btn_guardian_student_no.Checked == true)
{
using(SqlConnection cnn = new SqlConnection(......))
using(SqlCommand cmd = new SqlCommand(#"SELECT * FROM Guardian
WHERE STUDENT_NO = #student_no", cnn))
{
cnn.Open();
cnn.Add("#student_no", SqlDbType.Int).Value = Convert.ToInt32(txt_bx_guardian_student_search.Text);
using(SqlDataReader rd = new cmd.ExecuteReader())
{
if(!rd.HasRows)
lbl_guardian_student_search.Text = "No Guardian record exists for this student. Please enter the Guardian Information";
else
{
rdr.Read();
txtBox1.Text = rdr.Item["DBFieldName1"].ToString();
txtBox2.Text = rdr.Item["DBFieldName2"].ToString();
}
}
}
}
}
Note that I have moved the global connection object inside the code making it a local variable that is initialized inside a using block as well the command and the reader. They are disposable objects and should be disposed when you have finished with them. The Using Statement ensure correct dispose of these objects
Also note that keeping a connection opened all the time of your application is really a big NO-NO in database server applications. You hinder the server ability to serve more requests if you keep your connection constantly open. And there is no great penalty in restoring the server connection because ADO.NET has an infrastructure called Connection Pooling that allows you to restore your connection immediately
namespace PCMS
{
public partial class frmPlayerInterface : Form
{
private OleDbConnection con = new OleDbConnection();
OleDbCommand com = new OleDbCommand();
private DataTable dt = new DataTable();
public frmPlayerInterface(string getUser)
{
InitializeComponent();
con.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Projects\SDP\PCMS\SDP.accdb";
lblUser.Text = getUser;
}
private void btnEnquire_Click(object sender, EventArgs e)
{
frmEnquire frmenq = new frmEnquire();
frmenq.ShowDialog();
}
private void btnTopUp1_Click(object sender, EventArgs e)
{
frmTopUp frmTU = new frmTopUp();
frmTU.ShowDialog();
}
private void frmPlayerInterface_Load(object sender, EventArgs e)
{
con.Open();
OleDbCommand comm = new OleDbCommand();
String sql = "select Balance from PlayerAccount where Player_User=#user";
comm.Parameters.Add(new OleDbParameter("user", lblUser.Text));
comm.CommandText = sql;
OleDbDataReader cursor = comm.ExecuteReader();
while (cursor.Read())
{
lblBalance.Text = cursor["Balance"].ToString();
}
con.Close();
}
}
}
Hey sorry guys asking this again but ive been trying this for the past three hours and wave the white flag. Still getting the same error.
I just want to have the selected balance value from the database to be shown in the label.
Thanks ><
You're not associating the connection with the command object:
con.Open();
String sql = "select Balance from PlayerAccount where Player_User=#user";
OleDbCommand comm = new OleDbCommand(sql, con);
Note that reusing a connection is not always the best design. Connections are pooled in .NET, so recreating them is generally not an expensive operation. A better design would be to store the connection string as a class property then just create a connection when you need it:
private string ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Projects\SDP\PCMS\SDP.accdb";
// or better yet - pull form app.config...
and when you use it:
String sql = "select Balance from PlayerAccount where Player_User=#user";
using(OleDbConnection con = new OleDbConnection(ConnectionString))
{
con.Open();
using(OleDbCommand comm = new OleDbCommand(sql, con))
{
... Add parameters, execute query, return results
}
}