Close MySQL Connection outside Function - c#

I currently have a little app sends a lot of different MySQL Queries to the server. My idea was to wrap the connection, the query and the read to a function with only the actual query as a parameter.
Here is what I got:
public static MySqlDataReader mySqlRead(string cmdText)
{
string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
MySqlConnection conn = new MySqlConnection(connString);
MySqlCommand command = conn.CreateCommand();
command.CommandText = cmdText;
try
{
conn.Open();
MySqlDataReader reader = command.ExecuteReader();
return reader;
}
catch (MySqlException)
{
throw;
}
}
I connect and send the query here:
private void btnLogin_Click(object sender, EventArgs e)
{
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
MySqlDataReader orRead = ORFunc.mySqlRead("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");
while (orRead.Read())
{
MessageBox.Show(orRead["id"].ToString());
}
}
Works like a charm... BUT, as you can see above, the connection is never closed. When I add the conn.Close() behind the .ExecuteReader() the reader is empty and everything after return is of course useless.
Maybe it's a stupid question but I'm rather new to C# so please be generous, any hint is appreciated.
cheers,
PrimuS

I had a similar problem in JAVA recently, but I think the same will work for you. Essentially, you can create a class that represents a "SqlCall" object (or something). The class would have accessible members including the connection and the results. The ctor for the class would take in your query text.
Then, all you would have to do is create a new instance of that class, run the query in a method in that class (which would set and/or return the results), GET the results, and then when you are done, call close() on your class (which would then have to be coded such that it closes the connection held internally).
Technically, a better way to do this is to EXTEND the connection class itself, but as you are new to C#, I will not go into the details of doing so.
As I was writing the code below, I realized I may have not actually answered your question. But there's no point in backing out now, so here's what I have:
public class SqlCall {
private static connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
private MySqlConnection conn;
private MySqlCommand command;
private MySqlDataReader reader;
public SqlCall(String query) {
conn = new MySqlConnection(connString);
command = conn.CreateCommand();
command.CommandText = query;
}
public MySqlDataReader execute() throws Exception {
conn.Open();
reader = command.ExecuteReader();
return reader;
}
public void close() {
reader.close();
conn.close();
}
}
Your login code would be:
private void btnLogin_Click(object sender, EventArgs e) {
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
SqlCall sqlcall = new SqlCall("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");
try {
MySqlDataReader orRead = sqlcall.execute();
while (orRead.Read())
{
MessageBox.Show(orRead["id"].ToString());
}
sqlcall.close();
} catch (Exception ex) {
// dostuff
}
}
The point is, unless you copy the data into a new datatable at the very beginning, you'll have to keep the connection open.
On a separate note, YOUR CODE IS PRONE TO SQL INJECTION. Don't know what that is? An example: if I said my username was ';DROP TABLE orUsers;--, then your entire user database would be gone. Look into stored procedures if you want a (very healthy) way around this.

You have difficulties because your idea works against the pattern expected by programs that connects to a database in NET Framework.
Usually, in this pattern you have a method that
INITIALIZE/OPEN/USE/CLOSE/DESTROY
the ADO.NET objects connected to the work required to extract or update data
Also your code has a serious problem called Sql Injection (see this famous explanation) because when you concatenate strings to form your command text you have no defense against a malicious user that try to attack your database
private void btnLogin_Click(object sender, EventArgs e)
{
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
MySqlParameter p1 = new MySqlParameter("#uname", username);
MySqlParameter p2 = new MySqlParameter("#pass", pass);
string cmdText = "SELECT * FROM orUsers WHERE username = #uname AND pass = #pass"
DataTable dt = ORFunc.GetTable(cmdText, p1, p2);
foreach(DataRow r in dt.Rows)
{
Console.WriteLine(r["ID"].ToString());
}
}
public static DataTable GetTable(string cmdText, params MySqlParameter[] prms)
{
string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
// This is the INITIALIZE part
using(MySqlConnection conn = new MySqlConnection(connString))
using(MySqlCommand command = new MySqlCommand(cmdText, conn))
{
// OPEN
conn.Open();
DataTable dt = new DataTable();
command.Parameters.AddRange(prms);
// USE
MySqlDataReader reader = command.ExecuteReader();
dt.Load(reader);
return dt;
} // The closing brace of the using statement is the CLOSE/DESTROY part of the pattern
}
Of course this is a generic example and in my real work I don't use very often these generic methods and prefer to write specialized data access code that return the base object needed to the upper layer of code

Related

How to connect to mysql without using plain text?

I'm trying to make it so i can use username/password i've set in the properties/settings to connect to mysql as i dont want to store mysql details in plaintext here what i'm using but not sure how to go about reading the username/password from properties, Could anyone help? thanks
private void PassTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
try
{
string MyConnection = "datasource=localhost;port=3309;username=user;password=pass123";
MySqlConnection MyConn = new MySqlConnection(MyConnection);
MySqlCommand MyCommand = new MySqlCommand("select * from applogin.users where UserId='" + this.UserTextBox.Text + "' and UserPassword='" + this.PassTextBox.Text + "' ;", MyConn);
MySqlDataReader MyReader;
recby = UserTextBox.Text;
MyConn.Open();
MyReader = MyCommand.ExecuteReader();
int count = 0;
while (MyReader.Read())
{
Console.WriteLine(MyReader[count]);
count++;
}
if (count == 1)
{
MessageBox.Show("Accepted, Welcome!");
this.Hide();
Form5 f5 = new Form5();
f5.ShowDialog();
}
else if (count > 1)
{
MessageBox.Show("Duplicate Username and passwor.\nAccess denied.");
}
else
{
MessageBox.Show("Username and password is incorrect.\nPleas try again.");
}
MyConn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
class DBConnect
{
private MySqlConnection connection;
private string server;
private string database;
private string uid;
private string password;
//Constructor
public DBConnect()
{
Initialize();
}
//Initialize values
private void Initialize()
{
server = "localhost";
database = "connectcsharptomysql";
uid = "username";
password = "password";
string connectionString;
connectionString = "SERVER=" + server + ";" + "DATABASE=" +
database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
connection = new MySqlConnection(connectionString);
}
//open connection to database
private bool OpenConnection()
{
bool flag=false;
if (this.OpenConnection() == true)
{
//Create Mysql Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//ExecuteScalar will return one value
Count = int.Parse(cmd.ExecuteScalar()+"");
flag=true;
//close Connection
this.CloseConnection();
return flag;
}
else
{
return flag;
}
}
}
I would rather recommend to read password from an environment variable instead of hardcoding it in the code.
This is a broad topic.
Mainly, it is "how to store a 'secret' well?"
I'll give some hints.
First, in dotnet CORE, there is new stuff.
https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1&tabs=windows
That is for local development, BUT AT LEAST forces you to think about where that secret is being stored.
Using .ini, .config, .txt files and keeping a secret in plain text IS NOT secure for production scenarios.
So then you have to start thinking about "how is my code deployed?"
If you are deploying to Azure, there is Azure Key Vault.
If you are deploying to a machine/VM, then you have to consider something more robust, like Hashicorp Vault.
But you're going to get a few "quick fix" answers. You need to really consider where you keep this secret for your deployed code.
If I read your question correctly, you don't care about the connection string userid/password but about the users login credentials.
This is based on your line:
MySqlCommand MyCommand = new MySqlCommand("select * from applogin.users where UserId='" + this.UserTextBox.Text + "' and UserPassword='" + this.PassTextBox.Text + "' ;", MyConn);
And it appears you are asking about how to encrypt the password so that someone can't monitor the communication. Of course, your connection string is already an issue, but back to what I believe is your question.
It's not clear if you are using the actual user name and password as entered or not. If so, the easiest option is to use 1 way encryption - and only store the encrypted values. Then your code would get the value of the textbox and encrypt it. Then execute a query with the encrypted value (preferably with those encrypted values as parameters, not inline text.)
The result would be a query that might look like:
select * from applogin.users where UserId='kajsdf09823rcfs98dsjssdf' and UserPassword='j;las9onq;p;9wf;kjadf' ;
and written correctly (assuming you have a method named encrypt that implements whatever encryption you choose):
userId = new SqlParameter("#userName", encrypt(this.UserTextBox.Text));
userpw = new SqlParameter("#userPw", encrypt(this.PassTextBox.Text));
cmd.Parameters.Add(userId);
cmd.Parameters.Add(userpw );
cmd.CommandText = "select * from applogin.users where UserId=#userName and UserPassword=#userPw ;";
There are other great suggestions as well, I encourage you to follow up on those as well.

SqlConnection in VS2015 C#

I am extremely new to C# web development (or any development for that matter) but I am trying to figure out how to save the results from a SQL query to a variable. I think I understand the process, but many of the examples I am finding on the Web use a SqlConnection statement. My copy of Visual Studio does not seem to have that command (pretty sure I am using the wrong word here). What am I missing either softwarewise or knowledgewise accomplish my task?
Thank you in advance for your help.
Dep
It depends on what you want to do: insert, update, get data. It also depends if you want to use an ORM library or not. I all depends. The code that I copy below is an example of how to retrieve a DataTable using Ado.Net (as you mentioned SqlConnection):
You have to use:
using System.Data;
using System.Data.SqlClient;
This is the code for retrieving a DataTable
private DataSet ExecuteDataset(string query)
{
var conn = new SqlConnection("Data Source=" + Server + ";Initial Catalog=" + Database + ";User Id=" + Username + ";Password=" + Password + ";");
DataSet ds;
try
{
conn.Open();
ds = new DataSet();
var da = new SqlDataAdapter(query, conn);
da.Fill(ds);
}
catch (Exception)
{
throw;
}
finally
{
conn.Dispose();
conn.Close();
}
return ds;
}
private DataSet ExecuteDataset(string query, SqlParameter[] parametros)
{
var conn = new SqlConnection("Data Source=" + Server + ";Initial Catalog=" + Database + ";User Id=" + Username + ";Password=" + Password + ";");
DataSet ds;
try
{
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandText = query;
foreach (SqlParameter p in parametros)
{
command.Parameters.Add(p);
}
ds = new DataSet();
var da = new SqlDataAdapter(command);
da.Fill(ds);
}
catch (Exception)
{
throw;
}
finally
{
conn.Dispose();
conn.Close();
}
return ds;
}
This is the code for running a query that does not expect result with and without parameters:
private void ExecuteNonQuery(string query)
{
var conn = new SqlConnection("Data Source=" + Server + ";Initial Catalog=" + Database + ";User Id=" + Username + ";Password=" + Password + ";");
try
{
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandText = query;
command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
conn.Dispose();
conn.Close();
}
}
private void ExecuteNonQuery(string query, SqlParameter[] parametros)
{
var conn = new SqlConnection("Data Source=" + Server + ";Initial Catalog=" + Database + ";User Id=" + Username + ";Password=" + Password + ";");
try
{
conn.Open();
SqlCommand command = conn.CreateCommand();
command.CommandText = query;
foreach (SqlParameter p in parametros)
{
command.Parameters.Add(p);
}
command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
finally
{
conn.Dispose();
conn.Close();
}
}
Here is the simplest example I can think of, take note of the using statements and comments
using System;
using System.Data;
using System.Data.SqlClient;
namespace DataAccess
{
class Program
{
static void Main(string[] args)
{
//Use your database details here.
var connString = #"Server=localhost\SQL2014;Database=AdventureWorks2012;Trusted_Connection=True;";
//Enter query here, ExecuteScalar returns first column first row only
//If you need to return more records use ExecuteReader/ExecuteNonQuery instead
var query = #"SELECT [AccountNumber]
FROM [Purchasing].[Vendor]
where Name = #Name";
string accountNumber = string.Empty;
//Using statement automatically closes the connection so you don't need to call conn.Close()
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(query, conn);
//Replace #Name as parameter to avoid dependency injection
cmd.Parameters.Add("#Name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = "Michael";
try
{
conn.Open();
//Cast the return value to the string, if it's an integer then use (int)
accountNumber = (string)cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.WriteLine(accountNumber);
//ReadKey just to keep the console from closing
Console.ReadKey();
}
}
}
If you are really new to C# with SQL Server, I would recommend to start from scratch using one of the tutorials shown here. It provides a lot of information in a step-by-step manner:
The basics
Clearly definition of the core ceoncepts
How to setup dependencies to get started
How to choose between development models (code, model vs. database first)
How to write queries
and much more.
Using SqlConnection is a valid choice, but requires more effort in writing the queries for doing basic stuff like selecting, updating, deleting or inserting data. You actually have to construct the queries and take care to build and dispose the commands.
On a related note:
The new way of doing all database-related activities in C# or .NET would be to harness Entity Framework (EF), and try to move away from any ADO.NET-based code. The latter still exists and hasn't been marked as obsolete, though. You may want to use ADO.NET for small apps or for any PoC tasks. But, otherwise, EF is the way to go.
EF is an ORM, and is indeed built as a repository pattern and generates a conceptual layer for us to work with. All of the nuances of the connection and command are completely encapsulated from us. This way, we don't have to meddle with these bare-bones.
If you want to do it well, you have to edit a file named Web.config in your project and put something like this inside (with your own DB data):
<connectionStrings >
<add
name="myConnectionString"
connectionString="Server=myServerAddress;Database=myDataBase;User ID=myUsername;Password=myPassword;Trusted_Connection=False;"
providerName="System.Data.SqlClient"/>
</connectionStrings>
Then, in the code, you can use it with:
SqlConnection con = new SqlConnection(
WebConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString);
Finally you can do that you want with "con", for example:
string queryString = "SELECT name, surname FROM employees";
SqlCommand command = new SqlCommand(queryString, con);
con.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader["name"], reader["surname"]));
}
}
finally
{
reader.Close();
}
i use this nuget library.
https://www.nuget.org/packages/SqlServerDB_dotNET/
using SqlServerDB;
string server = #"INSTANCE\SQLEXPRESS";
string database = "DEMODB";
string username = "sa";
string password = "";
string connectionString = #"Data Source="+ server + ";Initial Catalog="+ database + "; Trusted_Connection=True;User ID="+ username + ";Password="+ password + "";
DBConnection db_conn = new DBConnection(connectionString);
Console.WriteLine("IsConnected: " + db_conn.IsConnected());
if (db_conn == null || !db_conn.IsConnected())
{
Console.WriteLine("Connessione non valida.");
return;
}
string sql = "SELECT ID, Message FROM Logs ORDER BY IDLic;";
DataTable dtLogs = db_conn.SelectTable(sql);
if (dtLogs == null || dtLogs.Rows.Count == 0)
return;
// Loop with the foreach keyword.
foreach (DataRow dr in dtLogs.Rows)
{
Console.WriteLine("Message: " + dr["Message"].ToString().Trim());
}

Retrieve data from database using C#

I'm trying to retrieve data from database in Microsoft visual studio 2013 . I am totally lost whether I am already able to connect to the database or not and I am not sure how to retrieve data using c# as I am totally new to c#.
I am also not sure where I should put the static void main method statement before or after connectDB() method.
private void connectDB()
{
// server = "172.20.129.159";
database = "eyetracker";
server = "localhost";
// uid = "ogamaaccess";
// password = "ogama";
uid = "root";
password = "root";
string connectionString;
connectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
c = new MySqlConnection(connectionString);
Console.WriteLine("Connected to database");
}
private bool OpenConnection()
{
try
{
c.Open();
Console.WriteLine("Connection Opened.");
return true;
}
catch (MySqlException)
{
return false;
}
You need to install the MySql NET Connector that provides the appropriate bits to connect to MySQL database.
After installing the provider you need to add a reference to MySql.Data.Dll and add the appropriate using statement to your code
using MySql.Data.MySqlClient;
You also need to change your connection string which could be found in here.
Connection code should look something close to this:
private void Login() // login method
{
string connectString = #"uid=<UserID>;password=<Password>;
server=<IPorDomainNameOfDatabase>;
database=<DatabaseNameOnServer>;";;
using(MySqlConnection cnn = new MySqlConnection(connectString))
{
try
{
cnn.Open();
}
catch (Exception e)
{
.....
}
}
}
Full code could look something like this (command should be edited according to data you want to retrieve):
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
command.CommandText = "Select <VALUE> from <TABLE> order by <ID> desc limit <0,1>;";
//Command to get query needed value from DataBase
connect.Open();
MySqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
var result = reader.GetString(0);
}
Note: I strongly suggest you to put connect.Open(); into TryCatch statment, since there are many things that can do wrong and your program will crash.

Connecting to the MDB Database C#

An unhandled exception of type 'System.Data.OleDb.OleDbException' occurred in System.Data.dll
Additional information: Could not use ''; file already in use
This is where the error points at:
da.Fill(dt);
The database is located at C:\ChattBankMDB.mdb on my computer.
Database: http://puu.sh/hjQj0/d86ede4c00.png
When I press the button1, I would like for the form to follow up and login on the Customer database else a messagebox.show will say failure to login.
Button on form:
public partial class CustLogin : Form
{
OleDbConnection db = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\ChattBankMDB.mdb");
OleDbDataAdapter da = new OleDbDataAdapter();
DataTable dt = new DataTable();
public CustLogin()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
da = new OleDbDataAdapter("Select CustID, CustPassword From Customers", db);
da.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
if (UserText.Text == dt.Rows[i]["CustID"] && PassText.Text == dt.Rows[i]["CustPassword"])
{
WelcomeCust f = new WelcomeCust();
this.Hide();
f.Show();
}
else
{
MessageBox.Show("FAILURE TRY AGAIN");
}
}
}
I've noticed a couple of potential issues:
Database Injection.
Password in plain text.
Utilizing SqlConnection.
A .mdb isn't a SQL database, it is actually a Microsoft Access database. So you'll want to actually use ADO.NET connection. So your code should actually be:
private readonly string dbConnection = ConfigurationManager.ConnectionStrings["..."].ConnectionString;
private const string query = "SELECT * FROM [Example] WHERE ([Id] = #Id);";
public void Example()
{
using(var connection = new OleDbConnection(dbConnection))
using(var command = new OleDbCommand(query, connection))
{
// Apply parameter, open connection, etc.
}
}
You utilize parameters to avoid a sub-query being introduced. As for your password in plain text you should take a look at BCrypt or another library for a Salt / Hash approach.
Then the change to the connection should alleviate your issue.
Your next issue I believe stems from the Fill being before you build your data table.
The database is an Acces database , so you need to use OleDB to connect to it.
Moreover, the query can cause errors.
Replace :
("Select* from Customers where CustID ='" + UserText.Text +"'
and CustPassword =" + PassText.Text + '"', conn)
By :
("Select * from Customers where CustID = '" + UserText.Text + "'
and CustPassword = '" + PassText.Text + "'", conn);

Data from c# application into Ms Access transferring incorrectly

...
using System.Data;
using System.Data.OleDb;
namespace accessloginapp
{
public partial class Ramen : Form
{
private OleDbConnection connection = new OleDbConnection();
public Ramen()
{
InitializeComponent();
connection.ConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\...\Users.accdb;Persist Security Info=False;";
}
private void btn_Save_Click(object sender, EventArgs e)
{
try{
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText =
"insert into userdata (Username,[Password]) values('" +
txt_Username + "','" + txt_Password + "')";
command.ExecuteNonQuery();
MessageBox.Show("Users added and saved");
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error" + ex);
}
}
}
}
I'm sorry if I do not understand much, I'm fairly to new to this. When I save data such as username and password in my application, the data is inserted as what I input but with added text, Example: I would send the username "Mark" to be inserted, but when I go to look at my database, it is put in as "System.Windows.Forms.TextBox, Text: Mark". How can I change this to only inserting the Username I Input?
You need to use Text property of textbox control, to fetch the actual text stored:-
command.CommandText = "insert into userdata (Username,[Password])
values('" + txt_Username.Text + "','" + txt_Password.Text + "')";
Apart from this please note your query is open for SQL Injection attack.
So, you should use Parameterized query something like this:-
command.CommandText = "insert into userdata (Username,[Password])
values(?,?)";
command.Parameters.Add("?",OleDbType.VarChar,20).Value = txt_Username.Text;
and similarly add parameter for #Password.

Categories