Principes de base de .NET : ORM (mappage relationnel d’objets)
Le mappage relationnel objet est très courant dans le développement Web, en particulier lorsqu’une base de données est impliquée, car un ORM facilite grandement le travail de développement. Dans cet article, nous verrons ce qu’est un ORM et comment l’implémenter dans une application .NET.
Le sujet ORM (Object Relational Mapping) est très connu dans l’environnement de développement Web .NET et sera très probablement une exigence dans une offre d’emploi dans le domaine. C’est pourquoi il est important que les développeurs se familiarisent avec ce concept : la mise en œuvre et la maintenance d’une base de données deviennent plus simples et plus rapides avec l’utilisation d’un ORM.
Qu’est-ce que l’ORM (Object Relational Mapping) ?
Le mappage relationnel objet, également connu sous le nom d’outil de mappage, en informatique, est une technique de programmation utilisée pour convertir des données entre des systèmes de types incompatibles via des langages de programmation orientés objet.
En termes simples, c’est comme si l’ORM créait une « base de données virtuelle » et répercutait cette « base de données virtuelle » dans une base de données traditionnelle, comme SQL Server par exemple.
Pourquoi l’ORM est-il utile ?
Le principal avantage de l’utilisation des ORM est de gagner du temps sur le développement et d’éliminer le besoin de répéter le code SQL.
Un autre avantage important est la traçabilité des modifications apportées à la base de données. Il existe des bibliothèques spécialisées dans les ORM qui enregistrent tout l’historique de la donnée depuis sa création jusqu’aux dernières modifications.
ORM dans le contexte .NET
Il est très courant de trouver des applications .NET qui utilisent un certain type d’ORM, qu’il soit créé par le développeur ou à l’aide d’un outil.
Par conséquent, l’ORM est un sujet important à apprendre pour les programmeurs .NET et est presque toujours couvert dans les entretiens pour les postes sur le terrain.
Il existe actuellement plusieurs bibliothèques qui implémentent des ORM disponibles pour travailler avec .NET. Certains sont open source tandis que d’autres sont payants, en plus d’avoir des fonctions pour les besoins les plus variés. Quelque chose de commun parmi les développeurs est la discussion sur le meilleur ORM, mais la vérité est qu’il n’est pas possible d’estimer lequel est le meilleur, car cela dépendra des besoins de chaque scénario.
Le noyau de l’Entity Framework
Comme dit précédemment, il est presque impossible d’indiquer quel serait le meilleur ORM, mais probablement le plus connu est l’Entity Framework Core, ou simplement EF Core.
EF Core est une version légère, extensible, open source et multiplateforme de la célèbre technologie d’accès aux données Entity Framework, développée et maintenue par Microsoft pour la plate-forme .NET.
EF Core permet aux développeurs .NET d’implémenter une base de données à l’aide d’objets. Il élimine également le besoin de la plupart des codes SQL qui doivent normalement être configurés dans les approches « Base de données d’abord ». EF Core prend également en charge plusieurs moteurs de base de données, notamment SQL Server, SQLite, MySQL et bien d’autres. Vous pouvez consulter la liste complète sur ce lien : Fournisseurs de bases de données.
EF Core contre EF6
Sans surprise, la nomenclature utilisée par Microsoft peut parfois être un peu déroutante, mais, comme toujours, tout est expliqué clairement. Dans le contexte d’Entity Framework, il existe deux versions principales : l’une est Entity Framework Core (EF Core) et l’autre est Entity Framework 6 (EF6).
Comme vu précédemment, EF Core est un mappeur de base de données objet moderne pour .NET, tandis que EF6 est un mappeur relationnel objet conçu pour .NET Framework mais prenant en charge .NET Core.
En termes simples, EF Core est utilisé pour les applications modernes, tandis que EF6 est recommandé pour les applications héritées où le framework a été développé dans .NET Framework, la première version de .NET.
Pour une vue plus détaillée du sujet, je vous recommande de lire cet article sur le site officiel de Microsoft : EF Core et EF6.
Implémentation d’EF Core dans une application ASP.NET Core
Dans cet article, nous allons créer une API minimale pour démontrer l’utilisation d’Entity Framework Core avec la base de données SQLite.
L’API minimale est une fonctionnalité disponible à partir de .NET 6. Pour créer le projet, vous pouvez utiliser le code ci-dessous, puis ouvrir le projet avec votre IDE préféré (cet exemple utilise Visual Studio 2022).
Vous pouvez accéder au code source complet du projet final à ce lien : Code source.
Obtenir les outils CLI .NET Core
Tout d’abord, vous devez installer l’interface de ligne de commande EF Core, elle peut être installée localement ou globalement, la plupart des développeurs préfèrent l’installer en mode global. Pour cela utilisez la commande ci-dessous :
dotnet tool install --global dotnet-ef
Structuration de la base de données
Notre base de données aura la structure suivante :
dotnet new web -o BlogManager
Création des classes de modèle
Dans un premier temps, nous allons créer les classes du modèle. Créez donc un nouveau dossier appelé « Modèles » et créez-y les classes suivantes :
Auteur
namespace BlogManager.Models;
public record Author
{
public Guid Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
Étiquette
namespace BlogManager.Models;
public record Tag
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid BlogPostId { get; set; }
}
BlogPost
namespace BlogManager.Models;
public record BlogPost
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<Tag> Tags { get; set; }
public DateTime PublishedDate { get; set; } = DateTime.Now;
public Guid AuthorId { get; set; }
public string CoverImage { get; set; }
public Author Author { get; set; }
}
BlogPostDto
namespace BlogManager.Models;
public record BlogPostDto(Guid Id, string Title, string Content, List<Tag> Tags, DateTime PublishedDate, string CoverImage, Author Author);
Téléchargement des dépendances
Pour télécharger les dépendances nécessaires, double-cliquez sur le fichier projet (.csproj) et ajoutez le code ci-dessous :
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
Création de la classe de contexte
Ensuite, nous allons créer la classe responsable de l’implémentation des paramètres de contexte de la base de données. Créez un nouveau dossier appelé « Data » et à l’intérieur de celui-ci la classe suivante :
AppDbContext
using BlogManager.Models;
using Microsoft.EntityFrameworkCore;
namespace BlogManager.Data;
public class AppDbContext : DbContext
{
public DbSet<BlogPost>? Posts { get; set; }
public DbSet<Tag>? Tags { get; set; }
public DbSet<Author>? Authors { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("DataSource = blogdb.db; Cache=Shared");
}
Exécution des commandes EF Core
Notre modélisation de données est prête, représentée par les classes Model. Notre base de données reflétera ces classes, l’ensemble de la base de données et le script de création des tables. Ceux-ci seront créés via certaines commandes disponibles dans la bibliothèque EF Core.
Pour exécuter les commandes, vous pouvez ouvrir la console Visual Studio en cliquant avec le bouton droit sur le projet et en choisissant l’option : « Ouvrir dans le terminal ». Vous pouvez également exécuter les commandes via le terminal de votre machine à la racine du projet.
Vous pouvez d’abord exécuter la commande ci-dessous :
dotnet ef migrations add InitialModel
La commande « dotnet ef migrations add » crée le modèle EF Core à partir des classes de domaine (entité) et les migrations créeront ou mettront à jour le schéma de base de données basé sur le modèle EF Core.
En termes simples, EF Core créera un nouveau dossier appelé « Migrations » et à l’intérieur il contiendra les fichiers chargés d’appliquer les créations et les modifications concernant la base de données avec toutes les relations entre les entités, en fonction des classes de modèle que nous avons créées dans le projet .
Ensuite, nous exécuterons la commande chargée d’appliquer le script créé avec la commande précédente et d’effectuer les créations et les modifications.
Alors, exécutez la commande ci-dessous :
dotnet ef database update
Notez que la commande précédente a créé une base de données – comme nous utilisons SQLite, un fichier avec l’extension .db a été créé à la racine du projet. Dans cet article, nous utiliserons l’éditeur de vue SQLite disponible gratuitement sur le Microsoft Store si vous utilisez Windows, mais vous pouvez en utiliser n’importe quel autre.
Dans l’image ci-dessous, nous pouvons voir dans l’éditeur SQL Viewer la structure des tables générées après l’application des commandes EF Core :
Exécution d’opérations CRUD avec EF Core
Il ne nous reste plus qu’à ajouter les configurations des classes principales et ajouter les méthodes responsables de CRUD. Pour cela, dans la classe Program, remplacez le code existant par le code ci-dessous :
using BlogManager.Data;
using BlogManager.Models;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
RegisterServices(builder.Services);
var app = builder.Build();
ConfigureApp(app);
void ConfigureApp(WebApplication app)
{
var ctx = app.Services.CreateScope().ServiceProvider.GetService<AppDbContext>();
ctx.Database.EnsureCreated();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseCors(builder => builder.AllowAnyOrigin());
}
void RegisterServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>();
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Blogs API",
Description = "Blog administration",
Version = "v1"
});
});
}
app.MapGet("/v1/posts", (AppDbContext context) =>
{
var posts = context.Posts;
if (!posts.Any())
return Results.NotFound();
var postsDto = posts.Select(b => new BlogPostDto(b.Id, b.Title, b.Content, b.Tags, b.PublishedDate, b.CoverImage, b.Author)).ToList();
return Results.Ok(postsDto);
}).Produces<BlogPostDto>();
app.MapPost("/v1/posts", (BlogPostDto createBlogPost, AppDbContext context) =>
{
try
{
var post = new BlogPost()
{
Id = Guid.NewGuid(),
Title = createBlogPost.Title,
Content = createBlogPost.Content,
Tags = createBlogPost.Tags,
PublishedDate = DateTime.UtcNow,
CoverImage = createBlogPost.CoverImage,
Author = createBlogPost.Author
};
context.Add(post);
context.SaveChanges();
return Results.Created($"v1/posts/{createBlogPost.Id}", createBlogPost);
}
catch (Exception ex)
{
return Results.BadRequest(ex);
}
}).Produces<BlogPostDto>();
app.MapPut("/v1/posts", (Guid id, BlogPost updateBlogPost, AppDbContext context) =>
{
try
{
var blogPost = context.Posts.Find(id);
if (blogPost is null)
return Results.NotFound();
context.Entry(blogPost).CurrentValues.SetValues(updateBlogPost);
context.SaveChanges();
return Results.NoContent();
}
catch (Exception ex)
{
return Results.BadRequest($"Error ocurred while puting to Post: {ex.Message}");
}
});
app.MapDelete("/v1/posts", (Guid id, AppDbContext context) =>
{
try
{
var post = context.Posts.Where(p => p.Id == id).FirstOrDefault();
if (post is null)
return Results.BadRequest($"Post not found to Id = {id}");
context.Remove(post);
context.SaveChanges();
return Results.NoContent();
}
catch (Exception ex)
{
return Results.BadRequest(ex);
}
});
app.Run();
Notez que les méthodes « ConfigureApp » et « RegisterServices » effectuent les configurations principales des classes, ainsi que la configuration de Swagger pour afficher l’interface API.
Les méthodes restantes sont responsables des opérations sur la base de données – pour récupérer et persister. Notez qu’EF Core dispose de méthodes très simples pour effectuer ces opérations, telles que « Ajouter » pour créer un nouvel enregistrement et « Enregistrer les modifications » pour enregistrer.
En d’autres termes, EF Core est une bibliothèque très robuste, complète et simple à utiliser.
Vous pouvez exécuter le projet et tester toutes les fonctions. Vous trouverez ci-dessous un exemple d’image de la fonction Get des enregistrements de la base de données par Fiddler Everywhere (un proxy de débogage Web sécurisé pour macOS, Windows et Linux.).
Conclusion
Dans cet article, nous avons vu l’importance d’utiliser un ORM et comment l’implémenter en pratique dans une application ASP.NET Core. Il existe plusieurs bibliothèques qui prennent en charge la création d’ORM. Dans cet article, nous avons couvert l’un des plus connus, EF Core.
Malgré les avantages de l’utilisation d’un ORM, ce n’est pas toujours le meilleur choix. Tout dépendra des besoins de chaque scénario.
Source link