Skip to main content

Refactorisation des couches d’accès aux données

Copilot Chat peut suggérer des moyens de découpler votre code d'accès aux données de votre logique commerciale, ce qui facilite la maintenance et l'évolution de l'application.

Les requêtes SQL codées en dur et le code d’accès aux données étroitement couplés peuvent rendre difficile la mise à l’échelle ou le changement de bases de données, et ils mènent souvent à une logique répétée.

Copilot Chat peut recommander la mise en œuvre d'un modèle de référentiel ou d'une couche d'accès aux données qui fait abstraction des interactions avec la base de données, ce qui rend le code plus modulaire et réduit la redondance. Il peut également aider à remanier les requêtes SQL pour en faire des versions optimisées, améliorant ainsi les performances et la maintenabilité.

Note

Les réponses décrites dans cet article sont des exemples. Copilot Chat Les réponses ne sont pas déterministes, il est donc possible que vous obteniez des réponses différentes de celles présentées ici.

Exemple de scénario

Ce code Python se connecte à une base de données SQLite, récupère un enregistrement utilisateur et retourne les données utilisateur. Toutefois, elle ne parvient pas à abstraction de la logique de connexion de base de données et utilise une requête codée en dur vulnérable à l’injection SQL.

import sqlite3

def get_user_by_id(user_id):
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute(f"SELECT display_name FROM users WHERE id = {user_id}")
    user = cursor.fetchone()
    conn.close()
    return user

Exemple d'invite 1

Vous pouvez commencer par poser Copilot une question générale sur la manière d'améliorer le code.

How can I improve this code to make it safe and easier to update and expand? List possible improvements but don't show revised code.

Exemple de réponse 1

Copilot fait plusieurs suggestions pour améliorer le code :

  • Abstrait les détails de la connexion à la base de données.
  • Utilisez des requêtes paramétrables pour empêcher l’injection SQL.
  • Utilisez un gestionnaire de contexte pour vous assurer que la connexion de base de données est correctement fermée si une erreur se produit.
  • Ajouter la gestion des erreurs.

Exemple d'invite 2

Vous pouvez utiliser la réponse à votre première invite pour écrire une invite plus spécifique.

Rewrite this code to make it more scalable and easier to maintain. Use a context manager. Avoid hardcoded SQL queries and tightly coupled data access code. Instead, use a repository pattern to abstract database interactions and make the code more modular and reusable. Where possible optimize the code to improve performance. Include error trapping, and make sure the code is not vulnerable to SQL injection.

Exemple de réponse 2

Copilot suggère de diviser le code en plusieurs fichiers distincts, chacun ayant un objectif spécifique. Il fournit également un exemple de code pour renvoyer les détails d’un utilisateur.

Les modifications suggérées implémentent un modèle de référentiel et utilisent des requêtes paramétrables pour éviter l’injection SQL.

# database.py
import sqlite3

class Database:
    def __init__(self, db_path):
        self.db_path = db_path

    def __enter__(self):
        self.conn = sqlite3.connect(self.db_path)
        self.cursor = self.conn.cursor()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type or exc_val or exc_tb:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()

# user_repository.py
class UserRepository:
    def __init__(self, db):
        self.db = db

    def get_user_by_id(self, user_id):
        try:
            self.db.cursor.execute("SELECT display_name FROM users WHERE id = ?", (user_id,))
            user = self.db.cursor.fetchone()
            return user
        except sqlite3.Error as e:
            print(f"An error occurred: {e}")
            return None

# main.py
from database import Database
from user_repository import UserRepository

def get_user_by_id(user_id):
    with Database('database.db') as db:
        user_repo = UserRepository(db)
        return user_repo.get_user_by_id(user_id)

# Example usage
if __name__ == "__main__":
    user = get_user_by_id(1)
    if user:
        print(user)
    else:
        print("User not found or an error occurred.")

Le code révisé améliore l’original de plusieurs façons :

  • Le code implémente désormais une couche d’accès aux données de base.
  • La classe Database gère la connexion à la base de données SQLite, implémentant le protocole de gestionnaire de contexte avec les méthodes __enter__ et __exit__ . Cela garantit que la connexion de base de données est correctement gérée, notamment la validation des transactions et la fermeture de la connexion.
  • La classe UserRepository encapsule la logique d’accès aux données utilisateur.
  • Les valeurs des requêtes sont paramétrées pour empêcher l’injection SQL.
  • Les erreurs sont interceptées, avec des détails imprimés sur la console.

Pour aller plus loin