ADO.NET
Utilisation d'une base de données avec C#
Notions théoriques
ADO.NET : accès aux bases de données
ADO.NET est la bibliothèque standard de .NET pour accéder aux bases de données relationnelles. Pour MySQL/MariaDB, on utilise le paquet MySql.Data.
dotnet add package MySql.Data
Connexion à MySQL
using MySql.Data.MySqlClient;
string connexion = "Server=localhost;Port=3306;Database=ma_base;Uid=root;Pwd=secret;";
using var conn = new MySqlConnection(connexion);
conn.Open();
Console.WriteLine("Connexion réussie !");
// conn.Dispose() appelé automatiquement par using
string host = Environment.GetEnvironmentVariable("DB_HOST") ?? "localhost";
string db = Environment.GetEnvironmentVariable("DB_NAME") ?? "mabase";
string user = Environment.GetEnvironmentVariable("DB_USER") ?? "root";
string pwd = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "";
string connexion = $"Server={host};Database={db};Uid={user};Pwd={pwd};";
Lire des données avec ExecuteReader
using var conn = new MySqlConnection(connexion);
conn.Open();
string sql = "SELECT id, nom, note FROM eleves ORDER BY nom";
using var cmd = new MySqlCommand(sql, conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
int id = reader.GetInt32("id");
string nom = reader.GetString("nom");
double note = reader.GetDouble("note");
Console.WriteLine($" {id} — {nom} : {note:F1}");
}
Requêtes paramétrées — OBLIGATOIRE contre l'injection SQL
La concaténation de variables dans une requête SQL est une faille de sécurité majeure (injection SQL). Utilisez toujours des paramètres nommés :
// DANGEREUX — injection SQL possible !
string sql = $"SELECT * FROM eleves WHERE nom = '{nomSaisi}'";
// SÉCURISÉ — paramètre nommé
string sql = "SELECT * FROM eleves WHERE nom = @nom";
cmd.Parameters.AddWithValue("@nom", nomSaisi);
Insérer avec ExecuteNonQuery
string sql = "INSERT INTO eleves (nom, note) VALUES (@nom, @note)";
using var cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@nom", "Alice");
cmd.Parameters.AddWithValue("@note", 15.5);
int nbLignes = cmd.ExecuteNonQuery();
Console.WriteLine($"{nbLignes} ligne(s) insérée(s).");
Lire une valeur unique avec ExecuteScalar
string sql = "SELECT COUNT(*) FROM eleves";
using var cmd = new MySqlCommand(sql, conn);
long count = (long)cmd.ExecuteScalar()!;
Console.WriteLine($"Nombre d'élèves : {count}");
string sqlMoy = "SELECT AVG(note) FROM eleves";
using var cmdMoy = new MySqlCommand(sqlMoy, conn);
double? moyenne = (double?)cmdMoy.ExecuteScalar();
Console.WriteLine($"Moyenne : {moyenne:F2}");
Le pattern DAO
Le DAO (Data Access Object) centralise toutes les opérations SQL liées à une table dans une classe dédiée :
class EleveDao
{
private readonly string _connexion;
public EleveDao(string connexion)
{
_connexion = connexion;
}
public void Insert(string nom, double note)
{
using var conn = new MySqlConnection(_connexion);
conn.Open();
string sql = "INSERT INTO eleves (nom, note) VALUES (@nom, @note)";
using var cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@nom", nom);
cmd.Parameters.AddWithValue("@note", note);
cmd.ExecuteNonQuery();
}
public List<(int Id, string Nom, double Note)> GetAll()
{
var liste = new List<(int, string, double)>();
using var conn = new MySqlConnection(_connexion);
conn.Open();
using var cmd = new MySqlCommand("SELECT id, nom, note FROM eleves", conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
liste.Add((reader.GetInt32("id"), reader.GetString("nom"), reader.GetDouble("note")));
return liste;
}
}
Exemple pratique
using MySql.Data.MySqlClient;
// Configuration depuis l'environnement
string host = Environment.GetEnvironmentVariable("DB_HOST") ?? "localhost";
string db = Environment.GetEnvironmentVariable("DB_NAME") ?? "bts_sio";
string user = Environment.GetEnvironmentVariable("DB_USER") ?? "root";
string pwd = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "";
string connexion = $"Server={host};Database={db};Uid={user};Pwd={pwd};";
try
{
using var conn = new MySqlConnection(connexion);
conn.Open();
Console.WriteLine($"Connecté à {db} sur {host}");
// Créer la table si elle n'existe pas
string createSql = """
CREATE TABLE IF NOT EXISTS eleves (
id INT AUTO_INCREMENT PRIMARY KEY,
nom VARCHAR(100) NOT NULL,
note DOUBLE
)
""";
using var createCmd = new MySqlCommand(createSql, conn);
createCmd.ExecuteNonQuery();
// Insérer des élèves
var donnees = new[] { ("Alice", 15.5), ("Bob", 12.0), ("Charlie", 17.5) };
foreach (var (nom, note) in donnees)
{
using var cmd = new MySqlCommand("INSERT INTO eleves (nom, note) VALUES (@nom, @note)", conn);
cmd.Parameters.AddWithValue("@nom", nom);
cmd.Parameters.AddWithValue("@note", note);
cmd.ExecuteNonQuery();
}
// Lire tous les élèves
using var reader = new MySqlCommand("SELECT id, nom, note FROM eleves ORDER BY note DESC", conn).ExecuteReader();
Console.WriteLine("\n=== Élèves ===");
while (reader.Read())
Console.WriteLine($" [{reader.GetInt32("id")}] {reader.GetString("nom"),-15} {reader.GetDouble("note"):F1}/20");
}
catch (MySqlException ex)
{
Console.Error.WriteLine($"Erreur MySQL : {ex.Message}");
}
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Vous allez créer une classe DaoEleve avec les opérations CRUD de base.
Étape 1 — Créer la table si elle n'existe pas
Ajoutez la méthode CreerTableSiInexistante() dans la classe DaoEleve.
CREATE TABLE IF NOT EXISTS est idempotent : vous pouvez l'appeler autant de fois que vous voulez sans erreur. C'est une bonne pratique de l'appeler au démarrage de l'application pour s'assurer que la structure est en place, sans risque d'erreur si la table existe déjà.
Étape 2 — Insérer un élève
Ajoutez la méthode Insert() avec des paramètres nommés.
N'écrivez jamais $"INSERT ... VALUES ('{nom}')". Utilisez toujours @nom avec AddWithValue. Les paramètres protègent contre l'injection SQL et gèrent automatiquement l'échappement des caractères spéciaux (apostrophes, guillemets...).
Étape 3 — Récupérer tous les élèves
Ajoutez la méthode GetAll() qui retourne une liste.
Retourner List<(int Id, string Nom, double Note)> (tuple nommé) plutôt qu'un List<object[]> ou un DataTable rend le code appelant plus lisible et bénéficie de la vérification de types à la compilation. Pour les classes plus complexes, créez une vraie classe Eleve.