Aller au contenu principal

9-4 Créer un premier Repository

Créer la classe EvenementRepository

Afin de contenir les fonctions permettant d'interagir avec la portion Evenements de la BD, on créera une classe EvenementRepository.

info

On nomme les classes de Repository au singulier:

  • EvenementRepository
  • ProduitRepository
  • ...
  1. Clic droit Repositories -> Add -> Class/Interface Imgur
  2. Nommer la classe EvenementRepository. Imgur

Transfert de la requête pour obtenir tous les événements

La requête pour obtenir la liste d'événements est contenue dans le contrôleur EvenementsController.

L'objectif est de déplacer cette requête à l'extérieur du contrôleur, dans un repository.

Constructeur EvenementRepository

Commençons par un constructeur qui recevra la configuration de l'application pour récupérer le ConnectionString.

Snowfall.Data/Repositories/EvenementRepository.cs
public class EvenementRepository
{
private IConfiguration _configuration;

public EvenementRepository(IConfiguration configuration)
{
_configuration = configuration;
}
//...
info

L'objet de configuration se conformant au contrat d'interface IConfiguration est l'objet permettant, entre autres, de récupérer le ConnectionString pour se connecter à la BD.

Fonction GetAll() pour obtenir les événements

Créons la fonction GetAll() qui sera responsable de récupérer tous les événements de la base de données et d'en faire le retour.

info

Vous remarquerez qu'on utilisera des noms de fonction en anglais pour les fonctions de récupération de données standard. En effet, une certaine nomenclature de base existe dans le monde .NET pour ces fonctions:

  • GetAll()
  • FindById()
  • Create()
  • Delete()
  • ...

Plutôt que de traduire pour le simple fait de traduire, je préfère utiliser les conventions: quelqu'un qui tombe dans votre projet, mais familier avec .NET, pourra s'y retrouver facilement et à l'inverse, d'autres projets .NET utilisant de standard vous sembleront familiers.

Snowfall.Data/Repositories/EvenementRepository.cs
//...
public async Task<List<Evenement>> GetAll()
{
string sql = @"
SELECT * from evenements;
";

using (IDbConnection connection = new NpgsqlConnection(
_configuration.GetConnectionString("AppDatabaseConnection")))
{
IEnumerable<Evenement> evenements = await connection.QueryAsync<Evenement>(sql);
return evenements.ToList();
}
}
//...
À propos de cette fonction
  • async Task<List<Evenement>>. Plutôt qu'une fonction classique, on crée une fonction asynchrone. L'utilisation du terme async indique que la fonction peut être exécutée de façon asynchrone, ainsi sans bloquer le reste de l'exécution de l'application. Pour des tâches comme l'accès à la base de données, c'est très utile afin de paralléliser les opérations.
  • Task<List<Evenement>>. Justement, parlant de tâches! La documentation de Tasknous dit: "Represents an asynchronous operation that can return a value". Il s'agit simplement d'un type de retour pour les opérations asynchrones. Ce n'est pas différent que de retourner List<Evenement> dans une fonction normale. Vous n'aurez jamais à interagir avec l'objet Task directement, le tout est transparent et le type de retour sera bien List<Evenement>.
  • GetAll(). Une pratique courante est d'identifier les fonctions asynchrones en ajoutant le suffixe Async, comme GetAllAsync(). Par contre, cette pratique est utile lorsque dans la même classe il existe une même version synchrone et asynchrone. On évite ainsi la duplication de noms et il est clair pour le programmeur qu'il existe deux versions. Dans notre cas, nous utiliserons seulement des fonctions async et il devient donc redondant d'ajouter un suffixe Async partout.
  • await connection.QueryAsync. On utilise QueryAsync plutôt que Query puisque nous sommes dans une fonction asynchrone et que Dapper offre la possibilité de faire une requête asynchrone. await est utilisé pour attendre le retour de la tâche avant de faire l'assignation à la variable evenements

Le reste est sensiblement la même chose que dans le contrôleur.

Le code complet de la classe devrait ressembler à ceci:

Snowfall.Data/Repositories/EvenementRepository.cs
public class EvenementRepository
{
private IConfiguration _configuration;

public EvenementRepository(IConfiguration configuration)
{
_configuration = configuration;
}

public async Task<List<Evenement>> GetAll()
{
string sql = @"
SELECT * from evenements;
";

using (IDbConnection connection = new NpgsqlConnection(
_configuration.GetConnectionString("AppDatabaseConnection")))
{
IEnumerable<Evenement> evenements = await connection.QueryAsync<Evenement>(sql);
return evenements.ToList();
}
}
}

Modification de l'action Index() de EvenementsController

On peut maintenant tester si le repository est fonctionnel en y faisant appel dans le contrôleur EvenementsController.

Snowfall.Web.Mvc/Controllers/EvenementsController.cs
public async Task<IActionResult> Index()
{
EvenementRepository repo = new EvenementRepository(_configuration);
List<Evenement> evenements = await repo.GetAll();
return View(evenements);
}
info

async Task<IActionResult> Index(). Puisqu'on fait appel à une fonction asynchrone (GetAll() du repository), on transforme l'action du contrôleur en tâche asynchrone. Le principe est exactement le même, mais on remplace le type de retour par Task<Type>.

C'est un peu le même principe qu'une promise en JavaScript.

Lancer le projet

Vous pouvez essayer de lancer le projet et tout devrait compiler, puis afficher la liste d'événements sur la page d'accueil.

Modification de l'action Show()

Puisque la requête de BD a été déplacée à la couche Data pour l'action Index(), il serait pertinent de faire la même chose pour l'action Show(), soit l'action permettant d'afficher le détail d'un événement/compétition.

Ajout d'une fonction FindById() à EvenementRepository.

Premièrement, on ajoutera au repository une nouvelle fonction: FindById(). Le nom de la fonction est très descriptif et permet en effet d'obtenir un modèle à partir d'un identifiant.

Snowfall.Data/Repositories/EvenementRepository.cs
public async Task<Evenement?> FindById(int id)
{
string sql = @"
SELECT * from evenements
WHERE id = @Id;
";

using (IDbConnection connection = new NpgsqlConnection(
_configuration.GetConnectionString("AppDatabaseConnection")))
{
var param = new
{
Id = id
};
var evenement = await connection.QuerySingleOrDefaultAsync<Evenement>(sql, param);

return evenement;
}
}
info

Somme toute, on reprend le même code qu'anciennement dans le contrôleur. La requête est la même, la seule différence étant dans l'utilisation de fonctions async.

Modification de l'action Show() de EvenementsController

Finalement, tout comme

Snowfall.Web.Mvc/Controllers/EvenementsController.cs
public async Task<IActionResult> Show(int id, string? option)
{
EvenementRepository repo = new EvenementRepository(_configuration);
var evenement = await repo.FindById(id);

if (evenement == null) return NotFound();

return View(evenement);
}

Lancer le projet

Vous devriez pouvoir continuer d'accéder à une fiche de détail.

party cat