Échelle en bois appuyée sur une étagère

Entity Framework, Repository Pattern et Factory Pattern

Qu’est-ce qu’Entity Framework et quel est son lien avec les modèles Repository et Factory ? Plongeons dans le vif du sujet !

Entity Framework (ou EF en abrégé) dans les applications C# utilise souvent des principes équivalents au Factory Design Pattern, mais il ne s’agit pas d’une implémentation directe du modèle de conception. Au lieu de cela, nous considérons EF comme un bon exemple d’implémentation du Repository Pattern.

EF se concentre sur le mappage objet-relationnel (en anglais Object-Relational Mapping ou ORM) pour interagir avec les bases de données. Ensuite, il fait abstraction de la création et de la gestion du contexte des données et des entités, ce qui peut être considéré comme l’exploitation d’un comportement similaire à celui d’un Factory Pattern.

Qu’est-ce que le Factory Pattern en bref ?

Imaginez une pizzeria dotée de plusieurs stations de fabrication de pizzas.

Chaque poste (Concrete Factory) suit un processus général de fabrication de pizzas (Abstract Factory) mais peut créer différents types de pizzas.

Qu’il s’agisse d’une pizza margherita, d’une pizza pepperoni ou d’une pizza végétarienne, le processus de base reste le même, mais les ingrédients spécifiques et la préparation varient.

Le Factory Pattern fonctionne de manière très similaire dans la conception de logiciels.

Il s’agit d’un modèle de conception créative qui fournit une interface pour la création d’objets dans une superclasse, mais qui permet aux sous-classes de modifier le type d’objets qui seront créés.

Essayez de réfléchir aux endroits de votre propre code où vous pourriez avoir besoin d’un processus de création d’objets flexible. Où voyez-vous des modèles de création répétitifs qui pourraient bénéficier d’une approche plus abstraite ?

Qu’est-ce que le Repository Pattern en bref ?

Imaginez-le comme une bibliothèque :

  • Vous (client) demandez un livre
  • Le bibliothécaire (référentiel) sait exactement où le trouver.
  • Vous n’avez pas besoin de connaître l’organisation interne de la bibliothèque.
  • Le bibliothécaire s’occupe de tous les mécanismes complexes de recherche.

Cette approche garantit que la logique de base de votre application reste propre, flexible et indépendante des implémentations spécifiques de stockage de données.

Le modèle de référentiel fonctionne comme un bibliothécaire, abstrayant les détails complexes d’accès aux données. Il fournit une interface simple et propre, semblable à une collection, pour :

  • Récupérer des données
  • Ajouter de nouvelles entités
  • Mettre à jour des entités existantes
  • Supprimer des entités

Les avantages sont les suivants :

  • Il découple la logique commerciale de l’accès aux données
  • Il centralisation de la logique d’accès aux données
  • Il simplifie les tests et la maintenance
  • Il permet de passer facilement d’une technologie de stockage des données à une autre

Comment Entity Framework se rapproche du Factory Pattern tout en utilisant fortement le Repository Pattern

Nous pouvons considérer le DbContext comme une Factory qui crée des instances d’objets Entity lors de l’interrogation de la base de données. Lorsque vous effectuez une requête, le DbContext génère les entités qui correspondent aux conditions de la requête.

Cependant, EF s’utilise en conjonction avec le Repository Pattern, qui peut encapsuler la logique de création et d’accès, ce qui le fait ressembler au Factory Pattern.

Usage

Exemple avec EF

Voici un exemple qui montre comment Entity Framework peut utiliser des principes similaires au Factory Pattern dans une application C#.

Tout d’abord, nous avons besoin d’une classe d’entité :

1
2
3
4
5
6
public class Book
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Isbn { get; set; }
}

Et d’une classe DbContext:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using Microsoft.EntityFrameworkCore;

public class BookContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("YourConnectionStringHere");
    }
}

Ensuite, on définit l’interface IRepository que toutes les classes de référentiel concrètes utiliseront :

1
2
3
4
5
6
7
8
public interface IRepository<T> where T : class
{
    IEnumerable<T> GetAll();
    T GetById(int id);
    void Add(T entity);
    void Delete(T entity);
    void Save();
}

Par exemple, la classe BookRepository l’implémente :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class BookRepository : IRepository<Book>
{
    private readonly BookContext _context;

    public BookRepository(BookContext context)
    {
        _context = context;
    }

    public IEnumerable<Book> GetAll()
    {
        return _context.Books.ToList();
    }

    public Book GetById(int id)
    {
        return _context.Books.Find(id);
    }

    public void Add(Book entity)
    {
        _context.Books.Add(entity);
    }

    public void Delete(Book entity)
    {
        _context.Books.Remove(entity);
    }

    public void Save()
    {
        _context.SaveChanges();
    }
}

Utilisation du référentiel dans le code client

Dans le code client, vous pourriez consommer le référentiel de la façon suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Program
{
    static void Main()
    {
        using (var context = new BookContext())
        {
            var library = new BookRepository(context);

            // Ajouter un livre
            var book1 = new Book { Name = "The Handbook of Science and Technology Studies", Isbn = "9780262035682" };
            // Puis un autre
            var book2 = new Book { Name = "Technology Trends for 2024", Isbn = "9781098167950" };
            // Et un 3e
            var book3 = new Book { Name = "Modern Operating Systems", Isbn = "9780137618842" };
            library.Add(book1);
            library.Add(book2);
            library.Add(book3);
            library.Save();

            // Lire tous les livres
            var books = library.GetAll();
            foreach (var book in books)
            {
                Console.WriteLine($"ID du livre : {book.Id}, Titre : {book.Name}, ISBN : {book.Isbn}");
            }
        }
    }
}

Dans l’exemple, il y a des éléments qui ressemblent à ceux d’une usine :

DbContext comme une Factory

La classe BookContext joue un rôle de Factory en :

  • Créant des connexions à la base de données et configurant l’accès à la base de données via la méthode OnConfiguring().
  • Fournissant les propriétés DbSet<T> qui permettent de suivre et d’interroger les entités.
  • Abstraillant l’interaction avec la base de données et donc en encapsulant la complexité des opérations de base de données

Le référentiel comme variant de la Factory

L’interface IRepository<T> et la classe BookRepository démontrent le pattern Repository, qui partage des similarités avec le pattern Factory :

  • Abstraction : fournit une interface standardisée pour les opérations d’accès aux données
  • Découplage : séparation de la logique d’accès aux données et de la logique métier
  • Flexibilité : permet de changer facilement de source de données ou d’implémentation ORM

Des caractéristiques assimilables à une Factory

Il y a les éléments suivants :

  • Création d’objets : le BookRepository crée et gère des instances d’entités Book. Il fournit également des méthodes comme Add(), GetById(), et ainsi de suite, qui abstraient la création et la récupération d’objets.
  • Injection de dépendance : le référentiel accepte un DbContext dans son constructeur, ce qui permet une configuration flexible. Il supporte également un couplage lâche entre l’accès aux données et la logique métier.
  • Abstraction : l’interface IRepository<T> définit un contrat pour les méthodes d’accès aux données. Les implémentations concrètes peuvent varier sans affecter le code client.

En résumé…

Bien qu’Entity Framework ne soit pas une implémentation directe du Factory Pattern, il utilise des principes similaires en abstrayant la création et la gestion des entités et de leur contexte.

Le pattern Repository, souvent utilisé avec EF, encapsule la logique d’accès aux données. Il peut favoriser un comportement similaire à celui d’une Factory, aidant à gérer la création d’objets et l’interaction avec la base de données.

Suivez-moi !

Merci d’avoir lu cet article. Assurez-vous de me suivre sur X, de vous abonner à ma publication Substack et d’ajouter mon blog à vos favoris pour ne pas manquer les prochains articles.

Crédit: Photo de Jean Vella sur Unsplash.