Connection
string connStr = "Server=localhost;Port=3306;Database=inventory;User=root;Password=myPassword;";
using (MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
Console.WriteLine($"Connected: {conn.Database}");
Console.WriteLine($"Version: {conn.ServerVersion}");
Console.WriteLine($"State: {conn.State}");
}
// conn.Dispose() auto-called — connection returned to pool
Create Database and Table
conn.Open();
new MySqlCommand("CREATE DATABASE IF NOT EXISTS inventory;", conn)
.ExecuteNonQuery();
conn.ChangeDatabase("inventory");
string createTable = @"
CREATE TABLE IF NOT EXISTS Cars (
CarId INT AUTO_INCREMENT PRIMARY KEY,
Make VARCHAR(50) NOT NULL,
Color VARCHAR(50) NOT NULL,
PetName VARCHAR(50),
Price DECIMAL(10,2) DEFAULT 0
);";
new MySqlCommand(createTable, conn).ExecuteNonQuery();
Command Object - 3 Execute Methods
// ExecuteNonQuery() -> INSERT/UPDATE/DELETE (returns row count)
int rows = cmd.ExecuteNonQuery();
// ExecuteScalar() -> single value (first col, first row)
object count = new MySqlCommand("SELECT COUNT(*) FROM Cars;", conn)
.ExecuteScalar();
// ExecuteReader() -> multiple rows (DataReader)
MySqlDataReader reader = cmd.ExecuteReader();
DataReader — Connected, Forward-Only
Connection stays open while reading. Can only go forward. Fastest way to read data.
var cmd = new MySqlCommand("SELECT * FROM Cars;", conn);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// By index:
Console.WriteLine($"{reader[0]} | {reader[1]}");
// By column name:
Console.WriteLine($"{reader["CarId"]} | {reader["Make"]}");
// Strongly typed:
int id = reader.GetInt32("CarId");
string make = reader.GetString("Make");
decimal price = reader.GetDecimal("Price");
}
}
Parameterized Queries — SQL Injection Prevention
NEVER concatenate user input into SQL. ALWAYS use parameters.
// BAD — SQL Injection!
// string bad = $"SELECT * FROM Cars WHERE Make = '{userInput}'";
// GOOD — Parameterized:
var cmd = new MySqlCommand("SELECT * FROM Cars WHERE Make = @make;", conn);
cmd.Parameters.AddWithValue("@make", "BMW");
// INSERT with parameters:
var ins = new MySqlCommand(
"INSERT INTO Cars (Make, Color, PetName, Price) "
+ "VALUES (@make, @color, @name, @price);", conn);
ins.Parameters.AddWithValue("@make", "Toyota");
ins.Parameters.AddWithValue("@color", "White");
ins.Parameters.AddWithValue("@name", "Ghost");
ins.Parameters.AddWithValue("@price", 28000);
ins.ExecuteNonQuery();
// UPDATE:
var upd = new MySqlCommand(
"UPDATE Cars SET Color = @c WHERE CarId = @id;", conn);
upd.Parameters.AddWithValue("@c", "Red");
upd.Parameters.AddWithValue("@id", 1);
upd.ExecuteNonQuery();
// DELETE:
var del = new MySqlCommand(
"DELETE FROM Cars WHERE CarId = @id;", conn);
del.Parameters.AddWithValue("@id", 5);
del.ExecuteNonQuery();
Transactions — All or Nothing
conn.Open();
MySqlTransaction tx = conn.BeginTransaction();
try
{
var cmd1 = new MySqlCommand(
"UPDATE Cars SET Price = Price - 5000 WHERE CarId = 1;",
conn, tx);
cmd1.ExecuteNonQuery();
var cmd2 = new MySqlCommand(
"UPDATE Cars SET Price = Price + 5000 WHERE CarId = 2;",
conn, tx);
cmd2.ExecuteNonQuery();
tx.Commit(); // both succeeded
}
catch (Exception ex)
{
tx.Rollback(); // something failed, undo everything
}
Disconnected Layer — DataAdapter + DataSet
DataReader = connected, fast, forward-only. DataSet = disconnected, in-memory cache, editable.
MySqlDataAdapter adapter = new MySqlDataAdapter("SELECT * FROM Cars;", conn);
DataSet ds = new DataSet("InventoryDB");
adapter.Fill(ds, "Cars");
// Connection CLOSED — work with in-memory data
DataTable table = ds.Tables["Cars"];
foreach (DataRow row in table.Rows)
Console.WriteLine($"{row["CarId"]} | {row["Make"]}");
// Modify in-memory:
table.Rows[0]["Color"] = "Purple";
// Push changes back:
MySqlCommandBuilder builder = new(adapter);
adapter.Update(ds, "Cars");
Async Versions (Modern Way)
using (var conn = new MySqlConnection(connStr))
{
await conn.OpenAsync();
var cmd = new MySqlCommand("SELECT * FROM Cars;", conn);
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
Console.WriteLine($"{reader["Make"]}");
}
}
Helper Class — Repository Pattern
public class CarRepository
{
private readonly string _cs;
public CarRepository(string cs) => _cs = cs;
public async Task<List<Car>> GetAllAsync()
{
var cars = new List<Car>();
using var conn = new MySqlConnection(_cs);
await conn.OpenAsync();
var cmd = new MySqlCommand("SELECT * FROM Cars;", conn);
using var r = await cmd.ExecuteReaderAsync();
while (await r.ReadAsync())
cars.Add(new Car
{
CarId = r.GetInt32("CarId"),
Make = r.GetString("Make"),
Color = r.GetString("Color")
});
return cars;
}
public async Task InsertAsync(Car c)
{
using var conn = new MySqlConnection(_cs);
await conn.OpenAsync();
var cmd = new MySqlCommand(
"INSERT INTO Cars (Make,Color,PetName,Price) "
+ "VALUES (@m,@c,@n,@p);", conn);
cmd.Parameters.AddWithValue("@m", c.Make);
cmd.Parameters.AddWithValue("@c", c.Color);
cmd.Parameters.AddWithValue("@n", c.PetName);
cmd.Parameters.AddWithValue("@p", c.Price);
await cmd.ExecuteNonQueryAsync();
}
}