Développement d'une API Web avec le framework .NET5 ABP

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 + s
  • FullAuditedEntity ajoute 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 _depotProduits est injecté via l'injection de dépendances
  • Utilisation du modèle de programmation asynchrone avec async et await
  • 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 AutoMapFrom indique à 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 exemple 20230515083045_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 Produit sera 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.

Étiquettes: .NET5 ABP-framework Web-API C# Entity-Framework-Core

Publié le 4 juin à 18h07