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
.
On nomme les classes de Repository
au singulier:
EvenementRepository
ProduitRepository
- ...
Clic droit Repositories
->Add
->Class/Interface
- Nommer la classe
EvenementRepository
.
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
.
public class EvenementRepository
{
private IConfiguration _configuration;
public EvenementRepository(IConfiguration configuration)
{
_configuration = configuration;
}
//...
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.
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.
//...
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();
}
}
//...
async Task<List<Evenement>>
. Plutôt qu'une fonction classique, on crée une fonction asynchrone. L'utilisation du termeasync
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 deTask
nous 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 retournerList<Evenement>
dans une fonction normale. Vous n'aurez jamais à interagir avec l'objetTask
directement, le tout est transparent et le type de retour sera bienList<Evenement>
.GetAll()
. Une pratique courante est d'identifier les fonctions asynchrones en ajoutant le suffixeAsync
, commeGetAllAsync()
. 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 fonctionsasync
et il devient donc redondant d'ajouter un suffixeAsync
partout.await connection.QueryAsync
. On utiliseQueryAsync
plutôt queQuery
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 variableevenements
Le reste est sensiblement la même chose que dans le contrôleur.
Le code complet de la classe devrait ressembler à ceci:
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
.
public async Task<IActionResult> Index()
{
EvenementRepository repo = new EvenementRepository(_configuration);
List<Evenement> evenements = await repo.GetAll();
return View(evenements);
}
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.
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;
}
}
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
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.