0
votes

Dois-je utiliser un DbContext dans mon API Web .Net Core ou une interface de service?

On m'a donné une évaluation à compléter pour un entretien d'embauche et l'évaluation me demande de créer une API Web dans .Net Core. Je n'ai jamais créé d'API Web à partir de zéro dans .Net Core, j'ai donc parcouru de nombreux tutoriels et il y a une chose qui m'a beaucoup dérouté. Dans certains didacticiels, l'exemple utilise une interface de service pour gérer la logique métier entre le contrôleur et le modèle, par exemple:

https://www.freecodecamp.org/news/an-awesome-guide-on-how-to-build-restful-apis-with-asp-net-core-87b818123e28/

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
    private readonly InventoryContext _context;

    public ProductsController(InventoryContext context)
    {
        _context = context;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<Products>>> GetProducts()
    {
        return await _context.Products.ToListAsync();
    }

Et dans certains didacticiels, l'exemple utilise un DbContext dans le contrôleur pour implémenter les opérations CRUD pour le modèle, généralement générées automatiquement lors de l'ajout d'un échafaudage Entity Framework, par exemple:

https://www.syncfusion.com/blogs/post/how-to-build-crud-rest-apis-with-asp-net-core-3-1-and-entity-framework-core-create-jwt- tokens-and-secure-apis.aspx

[Route("/api/[controller]")]
public class CategoriesController : Controller
{
    private readonly ICategoryService _categoryService;
    private readonly IMapper _mapper;

    public CategoriesController(ICategoryService categoryService, IMapper mapper)
    {
        _categoryService = categoryService;
        _mapper = mapper;
    }

    [HttpGet]
    public async Task<IEnumerable<CategoryResource>> GetAllAsync()
    {
        var categories = await _categoryService.ListAsync();
        var resources = _mapper.Map<IEnumerable<Category>, IEnumerable<CategoryResource>>(categories);
        
        return resources;
    }

Je ne sais pas quelle est la bonne / meilleure façon et pourquoi? Est-ce une chose de Entity Framework? Cela dépend-il de la complexité des modèles ou de la relation entre les modèles?

La documentation et les exemples de .Net Core sont nombreux et faire plus de recherche ne fait que m'envoyer en rond. J'apprécierais vraiment que quelqu'un puisse m'expliquer exactement de quoi je traite ici.


0 commentaires

4 Réponses :


0
votes

Cela dépend de l'architecture et de la structure de votre application. Si le projet contient plusieurs couches, il vaut mieux utiliser la première approche.

Dans la première approche, l'injection de dépendances est utilisée, ce qui a donné la responsabilité de créer une instance de classe de service à l'interface de service. Il s'appelle IoC.

La 2ème approche est simple. Si aucune autre couche n'est utilisée, interagissez directement avec le modèle de base de données via le contexte pour obtenir les données de la base de données.


0 commentaires

0
votes

Parfois, les tables de base de données représentent exactement ce que les utilisateurs de votre service attendent. Très souvent, les tables de base de données ont des valeurs que vous ne souhaitez pas exporter vers les utilisateurs du service. Dans ces cas, vous souhaitez découpler les types de base de données des types que vous retournez à l'utilisateur. Si des personnes interrogent des données, les tables de la base de données sont traduites en types que vous renvoyez.

Supposons que vous ayez une base de données avec les écoles, les enseignants et les étudiants. Ils ont la relation un-à-plusieurs évidente entre les écoles et les enseignants et entre les écoles et les étudiants. Il existe une relation plusieurs-à-plusieurs entre les enseignants et les étudiants: chaque enseignant a zéro élève ou plus, et chaque élève est enseigné par zéro ou plusieurs enseignants.

Normalement, dans une base de données relationnelle, les relations un-à-plusieurs sont implémentées à l'aide d'une clé étrangère. La relation plusieurs à plusieurs est implémentée à l'aide d'une table de jonction.

Le découplage renvoie uniquement les données attendues par les utilisateurs

Supposons que vous puissiez interroger le service pour "Écoles avec leurs élèves". Une autre question: "Donnez-moi des professeurs avec leurs élèves". Les utilisateurs ne sont pas intéressés par la manière dont vous obtenez les résultats. Ils ne veulent pas savoir que vous utilisez des clés étrangères et des tables de jonction.

Le découplage vous permet de renvoyer des données qui ne sont pas dans la base de données

Le découplage vous donne la liberté de vous donner d'autres données que celles de la base de données.

"Donnez-moi les écoles de la ville X avec leur nombre total d'étudiants". Le nombre total d'étudiants ne se trouve nulle part dans le tableau des écoles.

Le découplage permet d'omettre les données sensibles

Cette séparation entre les données renvoyées et la table de la base de données vous donne la possibilité d'omettre certaines valeurs. Si les gens demandent "Donnez-moi les étudiants du professeur X", ils n'attendent pas la clé étrangère de l'école de l'étudiant. Vous pouvez également omettre les rapports de comportement des étudiants.

Le découplage permet de personnaliser les types pour différents utilisateurs

Un enseignant qui a besoin d'interroger des données pour ses étudiants aura probablement besoin de données différentes de celles des étudiants qui souhaitent connaître les adresses de leurs camarades. L'enseignant peut avoir besoin de connaître les notes. Un étudiant peut voir ses propres notes, mais pas les notes de ses camarades.

Le découplage permet de changer la base de données

Enfin, il vous donne la possibilité de changer la base de données sans avoir à changer l'interface du Service. Par exemple, supposons que les gens se plaignent que s'ils font une erreur et suppriment accidentellement un élément, ils ne peuvent plus être annulés. Une méthode populaire consiste à ajouter un DateTime? propriété DeletedDate . La méthode «Supprimer l'étudiant» attribuera une date à DeletedDate , elle ne supprimera pas l'étudiant. Si l'opérateur a fait une erreur, la restauration sera facile: attribuez simplement null à DeletedDate . Un processus distinct supprimera régulièrement, disons une fois par mois, les éléments dont la DeletedDate antérieure à il y a un an, éléments que personne ne voudra plus jamais restaurer.

Conclusion

Il est judicieux de découpler les tables de la base de données des données que vous renvoyez. C'est une conception pour le changement, il permet la réutilisation, il est plus facile pour les utilisateurs de comprendre les données, vous pouvez masquer votre structure interne. Ce dernier facilite également les tests unitaires.

Mappeur automatique ou DbContext?

Ce n'est pas la vraie différence. La différence est la suivante: voulez-vous utiliser LINQ pour traduire les données que vous avez récupérées dans le format que vous utilisez, ou le mappeur automatique?

IMHO Avec LINQ, il est plus facile de voir ce que vous faites lorsque vous traduisez les données. LINQ n'utilise pas non plus la réflexion, ce qui la rend plus rapide.


0 commentaires

0
votes

En bref, vous souhaitez utiliser une couche de service pour votre logique métier, tout comme votre premier exemple. Cela vous donnera un meilleur contrôle pour bon nombre des raisons que d'autres personnes ont déjà évoquées.


0 commentaires

0
votes

Le découplage peut sembler fastidieux au début, mais quand un projet se développe, il simplifie en fait d'apporter des modifications plus tard. Qu'il s'agisse d'une API Web, wcf, gRPC ou simplement des viewmodels classiques pour MVC, vous serez presque toujours mieux avec des objets de requête / réponse séparés (DTO si vous voulez).

Quand il s'agit d'utiliser Automapper ou non, je préfère écrire des extensions ou simplement mapper manuellement les données que je veux obtenir des erreurs de compilation si je renomme quelque chose.


0 commentaires