1. Fonctionnalités de l'API
Objectif : Rechercher des informations sur un produit en fonction de son nom
2. Création de l'entité Produit
AbpTraining.Core\Produits\Produit.cs
using Abp.Domain.Entities.Auditing;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace AbpTraining.Produits
{
// Spécification explicite du nom de la table, sinon le nom par défaut est le nom de la classe + s
[Table("Produit")]
public class Produit : FullAuditedEntity<long>
{
[Required]
[StringLength(128)]
public string Nom { get; set; }
public decimal Prix { get; set; }
}
}
[Table("Produit")]spécifie explicitement le nom de la table correspondant à l'entité. Sans spécification, ABP utilise par défaut le nom de la classe + sFullAuditedEntityajoute des champs d'audit automatiques : DateCreation, IdUtilisateurCreation, IdUtilisateurSuppression, DateSuppression, DateDerniereModification, IdDernierModificateur[Required]indique que le champ est obligatoire. Sans cette annotation, le champ peut être nul[StringLength(128)]limite la longueur maximale du champ à 128 caractères
3. Création du service de domaine Produit
AbpTraining.Core\Produits\ServiceDomaineProduit.cs
using Abp.Domain.Repositories;
using Abp.Domain.Services;
using System.Threading.Tasks;
using System.Linq;
using Abp.UI;
using Microsoft.EntityFrameworkCore;
namespace AbpTraining.Produits
{
public class ServiceDomaineProduit : DomainService
{
private readonly IRepository<Produit, long> _depotProduits;
public ServiceDomaineProduit(IRepository<Produit, long> depotProduits)
{
_depotProduits = depotProduits;
}
public async Task<Produit> ObtenirProduitParNom(string nom)
{
var requete = from p in _depotProduits.GetAll()
where p.Nom == nom
select p;
var produit = await requete.FirstOrDefaultAsync();
if (produit == null)
{
throw new UserFriendlyException($"Le produit ({nom}) n'existe pas");
}
if (produit.Prix < 0)
{
throw new UserFriendlyException($"Le prix du produit ({nom}) est négatif, vérification requise");
}
return produit;
}
}
}
- Les services de domaine doivent hériter de
DomainService - Le dépôt de données
_depotProduitsest injecté via l'injection de dépendances - Utilisation du modèle de programmation asynchrone avec
asyncetawait - Les requêtes sont effectuées avec LINQ to SQL
- Pour retourner des erreurs métier au client, on utilise
UserFriendlyException
4. Création du service d'application Produit
4.1 Définition des DTO
AbpTraining.Application\Produits\Dto\DtoProduit
using Abp.AutoMapper;
namespace AbpTraining.Produits.Dto
{
[AutoMapFrom(typeof(Produit))]
public class DtoProduit
{
public string Nom { get; set; }
public decimal Prix { get; set; }
}
}
- L'atribut
AutoMapFromindique à partir de quelle classe le mappage automatique vers la classe actuelle peut être effectué, évitant ainsi une conversion manuelle des entités
AbpTraining.Application\Produits\Dto\RechercheProduitParNom
using System.ComponentModel.DataAnnotations;
namespace AbpTraining.Produits.Dto
{
public class RechercheProduitParNom
{
[Required]
public string Nom { get; set; }
}
}
[Required]- lorsque cet attribut est présent sur un objet d'entrée, ABP valide automatiquement que ce champ est obligatoire dans la requête
AbpTraining.Application\Produits\Dto\ResultatRechercheProduit
namespace AbpTraining.Produits.Dto
{
public class ResultatRechercheProduit : DtoProduit
{
}
}
4.2 Définition de l'interface du service d'application
AbpTraining.Application\Produits\IServiceAppProduit.cs
using Abp.Application.Services;
using AbpTraining.Produits.Dto;
using System.Threading.Tasks;
namespace AbpTraining.Produits
{
public interface IServiceAppProduit : IApplicationService
{
Task<ResultatRechercheProduit> RechercherProduitParNom(RechercheProduitParNom entree);
}
}
- Les interfaces de service d'application doivent hériter de
IApplicationService
4.3 Implémentation du service d'application Produit
AbpTraining.Application\Produits\ServiceAppProduit.cs
using System.Threading.Tasks;
namespace AbpTraining.Produits.Dto
{
public class ServiceAppProduit : AbpTrainingServiceAppBase, IServiceAppProduit
{
private readonly ServiceDomaineProduit _serviceDomaineProduit;
public ServiceAppProduit(ServiceDomaineProduit serviceDomaineProduit)
{
_serviceDomaineProduit = serviceDomaineProduit;
}
public async Task<ResultatRechercheProduit> RechercherProduitParNom(RechercheProduitParNom entree)
{
// 1. Conversion du DTO d'entrée en objet de domaine
// 2. Appel du service de domaine
var element = await _serviceDomaineProduit.ObtenirProduitParNom(entree.Nom);
// appel d'autres services de domaine si nécessaire
// 3. Conversion de l'objet de domaine en DTO de sortie
var resultat = ObjectMapper.Map<ResultatRechercheProduit>(element);
return resultat;
}
}
}
- Les implémentations de service d'application doivent hériter de
AbpTrainingServiceAppBase
5. Base de données
5.1 Mappage de l'entité de base de données
Dans le fichier AbpTraining.EntityFrameworkCore\\EntityFrameworkCore\\AbpTrainingDbContext.cs, ajoutez le code suivant :
public DbSet<Produit> Produits { get; set; }
5.2 Génération des fichiers de migration
Dans la console du gestionnaire de packages, exécutez la commande suivante pour générer le script de migration :
Add-Migration AjoutProduit -Verbose
- Après exécution, un nouveau fichier de script apparaîtra dans
AbpTraining.EntityFrameworkCore\\Migrations. Le nom du fichier généré sera par exemple20230515083045_AjoutProduit.cs(avec un préfixe d'horodatage différent à chaque fois)
5.3 Mise à jour de la base de données
Dans la console du gestionnaire de packages, exécutez la commande suivante pour synchroniser la nouvelle entité avec la base de données :
Update-Database -Verbose
- Une fois l'exécution terminée, une nouvelle table
Produitsera visible dans la base de données
6. Exécution et test du service
Définissez AbpTraining.Web.Host comme projet de démarrage et lancez-le directement dans Visual Studio. Dans la liste des API de Swagger, trouvez /api/services/app/Produit/RechercherProduitParNom pour commencer les tests.