Skip to main content

Umgestalten von Datenzugriffsschichten

Copilot-Chat kann Möglichkeiten vorschlagen, den Datenzugriffscode von deiner Geschäftslogik zu entkoppeln, wodurch eine Anwendung einfacher verwaltet und skaliert werden kann.

Hart kodierte SQL-Abfragen und fest eingebundener Code für den Datenzugriff können die Skalierung oder den Wechsel von Datenbanken erschweren und führen oft zu wiederholter Logik.

Copilot-Chat kann empfehlen, ein Repository-Muster oder eine Datenzugriffsschicht zu implementieren, die Datenbankinteraktionen abstrahiert, den Code modularer macht und Redundanzen reduziert. Es kann auch dabei helfen, SQL Abfragen in optimierte Versionen zu refactorieren und so die Leistung und Wartbarkeit zu verbessern.

Hinweis

Die in diesem Artikel beschriebenen Antworten sind Beispiele. Da die Copilot-Chat-Antworten nicht deterministisch sind, erhältst du möglicherweise andere Antworten als die hier dargestellten.

Beispielszenario

Dieser Python Code verbindet sich mit einer SQLite Datenbank, ruft einen Datensatz eines Benutzers ab und gibt die Benutzerdaten zurück. Allerdings wird die Logik der Datenbankverbindung nicht abstrahiert und eine fest kodierte Abfrage verwendet, die für SQL-Injection anfällig ist.

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

Beispielprompt 1

Du kannst damit beginnen, Copilot eine allgemeine Frage zu stellen, wie du den Code verbessern kannst.

Copilot Chat prompt
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.

Beispielantwort 1

Copilot macht mehrere Vorschläge zur Verbesserung des Codes:

  • Abstrahiere die Details der Datenbankverbindung.
  • Verwende parametrisierte Abfragen, um SQL-Injection zu verhindern.
  • Verwende einen Kontext-Manager, um sicherzustellen, dass die Datenbankverbindung im Fehlerfall ordnungsgemäß abgeschlossen wird.
  • Füge eine Fehlerbehandlung hinzu.

Beispielprompt 2

Du kannst die Antwort auf deinen ersten Prompt verwenden, um einen spezifischeren Prompt zu schreiben.

Copilot Chat prompt
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.

Beispielantwort 2

Copilot schlägt vor, den Code in verschiedene Dateien aufzuteilen, die jeweils einen bestimmten Zweck erfüllen. Außerdem gibt es einen Beispielcode für die Rückgabe der Daten eines Benutzers.

Die vorgeschlagenen Änderungen implementieren ein Repository-Muster und verwenden parametrisierte Abfragen, um SQL-Injection zu vermeiden.

# 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.")

Der überarbeitete Code ist in mehrfacher Hinsicht besser als das Original:

  • Der Code implementiert jetzt eine Basiszugriffsschicht.
  • Die Klasse Database handhabt die Verbindung zur SQLite-Datenbank und implementiert das Context Manager-Protokoll mit den Methoden __enter__ und __exit__. Dadurch wird sichergestellt, dass die Datenbankverbindung ordnungsgemäß verwaltet wird, einschließlich des Commitings von Transaktionen und des Schließens der Verbindung.
  • Die Klasse UserRepository kapselt die Logik für den Zugriff auf die Benutzerdaten.
  • Die Werte für die Abfragen sind parametrisiert, um SQL-Injection zu verhindern.
  • Fehler werden abgefangen und die Details auf der Konsole ausgegeben.

Weiterführende Themen