Base de données
Quand une application a besoin de stocker puis de retrouver des informations sur la durée elle utilise en général une base de donnée.
Le framework ASP.Net Core nous permet facilement de se connecter a une BDD puis d'y faire des requêtes.
Les base de données supporté
Il existe plusieurs base supporté nottament:
- SQL Server
- Postgres
- MariaDB
- Mysql
- Sqlite
- MongoDB
On peux très facilement se connecter a d'autre base en ajoutant une dépendance via le système Nuget.
Sqlite
Cette base est particulièrement intéressante car elle n'a pas besoin de serveur pour fonctionner. En effet le stockage des information se fait dans un fichier sur le disque.
Cela est très pratique pour effectuer des tests ou pour des petites applications.
BDD en concept objet
Le stockage et la récupération des informations utilise les classes défini dans le projet. Il faudra donc définir une classe qui représente l'objet que l'on souhaite stocker.
Voici un exemple pour ajouter une base Sqlite a un projet.
Pour cet exemple nous gérerons des Confiture
public class Confiture
{
public int Id { get; set; }
public string Fruit { get; set; }
public int Annee { get; set; }
public Confiture(int id, string fruit, int annee)
{
Id = id;
Fruit = fruit;
Annee = annee;
}
}
Maintenant que l'on a défini la structure de notre Confiture
passons a la définition de notre BDD.
Instalation des dépendances
Pour pouvoir sauvegarder nos données, nous allons utiliser l’entity framework.
C’est un framework qui s’occupe d’abstraire les requêtes vers la base pour nous.
Pour cela, il faut l’ajouter à notre projet. Nous en profiterons pour ajouter dotnet ef
qui permettra d’initialiser notre base de donnée
pour se faire, executer ces commandes dans le répértoire du projet depuis un terminal.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.*
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 8.*
dotnet add package Microsoft.EntityFrameworkCore.Design --version 8.*
dotnet tool install --global dotnet-ef --version 8.*
Définir notre connection a une BDD
Pour défini une BDD, il faut créer une classe qui hérite de DbContext
.
Le nom de cette nouvelle classe se termine en général par Context
.
La classe doit posséder un constructeur.
Puis pour chaque type d'élement à stocker on va créer une variable de type DbSet<Type>
Voila donc a quoi ressemble une classe basique
using Microsoft.EntityFrameworkCore;
namespace ConfitureApi.Models;
public class ConfitureContext : DbContext
{
public ConfitureContext(DbContextOptions<ConfitureContext> options)
: base(options)
{
}
public DbSet<Confiture> Confitures { get; set; } = null!;
}
Configurer Sqlite
Notre base n'est actuellement pas configuré pour utiliser Sqlite.
On va ajouter une méthode OnConfiguring
qui sera appelé pour configurer notre connection à la BDD.
Dans cette méthode on va spécifier que l'on désire utiliser Sqlite et on donnera le lien vers le fichier de la base.
public class ConfitureContext : DbContext
{
public ConfitureContext(DbContextOptions<ConfitureContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
// Connexion a la base sqlite
options.UseSqlite("Data Source=Confiture.db");
}
public DbSet<Confiture> Confitures { get; set; } = null!;
}
Initialiser la BDD au lancement
Pour que notre programme se connecte a notre BDD il faut le lui demander.
Cela se passe dans la phase de configuration dans Program.cs
builder.Services.AddDbContext<ConfitureContext>();
Les Migrations
On va créer un fichier de migration qui nous permettra de créer la structure de notre base.
Une migration est un fichier qui permet de faire passer une base d'un état A à un état B mais également de revenir à l'état A en efféctuant les opérations inverse.
Dans notre cas, ce premier fichier de migration va servir a créer notre table Confitures
avec ses champs.
La encore, c'est une commande qui va nous aider
dotnet ef migrations add InitialisationDeLaDB
Un nouveau dossier Migrations a du apparaitre. A l'intérieur 3 fichiers dont un qui devrait ressembler a ca :
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ConfitureApi.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// La liste des opérations à executer pour créer notre table Confitures
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
// Les opération inverse, donc ici la suppression de la table Confitures
}
}
}
Création de la BDD
Notre applicatif connait désormais notre BDD et s'y connecte au démarrage.
Mais elle n'existe pas encore cette BDD !
dotnet ef database update
Cette commande va commencer par créer notre BDD Sqlite si elle n'existe pas.
Puis elle va y appliquer les migrations une par une dans l'ordre.
Dans notre cas, une seul migration sera appliqué, pour créer notre table Confitures
.
Utiliser la BDD dans un controlleur
using Microsoft.EntityFrameworkCore;
Pour accéder à notre BDD dans un controlleur il faut injecter notre contexte dans le constructeur. On créer une variable privée pour stocker le contexte et on l'initialise dans le constructeur.
using ConfitureApi.Models;
namespace ConfitureApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class ConfitureController : ControllerBase
{
private readonly ConfitureContext _context;
public ConfitureController(ConfitureContext ctx)
{
_context = ctx;
}
}
Récupérer des données
Pour récupérer des données de notre BDD on va utiliser la méthode Find
de notre contexte.
[HttpGet("{id}")]
public async Task<ActionResult<Confiture>> GetConfiture(int id)
{
// on récupère la confiture correspondant a l'id
var confiture = await _context.Confitures.FindAsync(id);
if (confiture == null)
{
return NotFound();
}
// on retourne la confiture
return Ok(confiture);
}
Ajouter des données
Pour ajouter des données dans notre BDD on va utiliser la méthode Add
de notre contexte.
Puis on va appeler la méthode SaveChanges
pour enregistrer les modifications.
On peux ensuite retourner un code 201 pour indiquer que la création a bien eu lieu.
class ConfitureCreation
{
public string Fruit { get; set; }
public int Annee { get; set; }
}
[HttpPost]
public async Task<ActionResult<Confiture>> PostConfiture(ConfitureCreation confitureCreation)
{
// on créer une nouvelle confiture avec les informations reçu
Confiture confiture = new Confiture {
Fruit = confitureCreation.Fruit,
Annee = confitureCreation.Annee
};
// on l'ajoute a notre contexte (BDD)
_context.Confitures.Add(confiture);
// on enregistre les modifications dans la BDD ce qui remplira le champ Id de notre objet
await _context.SaveChangesAsync();
// on retourne un code 201 pour indiquer que la création a bien eu lieu
return CreatedAtAction(nameof(GetConfiture), new { id = confiture.Id }, confiture);
}
Mettre a jour des données
Pour mettre a jour des données dans notre BDD on va utiliser la méthode Update
de notre contexte.
Puis on va appeler la méthode SaveChanges
pour enregistrer les modifications.
On peux ensuite retourner un code 204 pour indiquer que la modification a bien eu lieu.
Le code est un peu plus complexe car il faut vérifier que l'objet que l'on souhaite modifier n'a pas été modifié entre temps.
On utilise donc un try catch pour gérer l'erreur DbUpdateConcurrencyException
.
[HttpPut("{id}")]
public async Task<IActionResult<Confiture>> PutConfiture(Confiture confitureUpdate)
{
// on récupère la confiture que l'on souhaite modifier
Confiture confiture = await _context.Confitures.FindAsync(confitureUpdate.Id);
if (confiture == null)
{
return NotFound();
}
// on met a jour les informations de la confiture
confiture.Fruit = confitureUpdate.Fruit;
confiture.Annee = confitureUpdate.Annee;
// on indique a notre contexte que l'objet a été modifié
_context.Entry(confiture).State = EntityState.Modified;
try
{
// on enregistre les modifications
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
// si une erreur de concurrence survient on retourne un code 500
return StatusCode(500, "Erreur de concurrence");
}
// on retourne un code 200 pour indiquer que la modification a bien eu lieu
return Ok(confiture);
}
Supprimer des données
Pour supprimer des données dans notre BDD on va utiliser la méthode Remove
de notre contexte.
Puis on va appeler la méthode SaveChanges
pour enregistrer les modifications.
On peux ensuite retourner un code 204 pour indiquer que la suppression a bien eu lieu.
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteConfiture(int id)
{
// on récupère la confiture que l'on souhaite supprimer
Confiture confiture = await _context.Confitures.FindAsync(id);
if (confiture == null)
{
return NotFound();
}
// on indique a notre contexte que l'objet a été supprimé
_context.Confitures.Remove(confiture);
// on enregistre les modifications
await _context.SaveChangesAsync();
// on retourne un code 204 pour indiquer que la suppression a bien eu lieu
return NoContent();
}
Conclusion
Les bases de données sont un élément essentiel de toute application.
ASP.Net Core nous permet de facilement nous connecter a une base et d'y effectuer des opérations sans intégrer de logique sépcifique à la base choisi.