Come devo incapsulare l'accesso al database?


10

Quali sono alcuni esempi di strutture di buona classe utilizzate per gestire l'accesso al database? Sono un fan dell'incapsulamento di classe e preferirei che i container (es. Auto) non eseguissero attività di database.

Vorrei anche la possibilità di inserire facilmente cose come una cache di database in futuro.

Prendo spesso lo schema delle classi container, complete di getter e setter per la validazione e l'accesso al database eseguiti da una singola classe singleton. Detto questo, questo spesso si confonde tra i due e diventa piuttosto confuso.

Scusate se la mia domanda è difficile da capire; Non sono assolutamente sicuro in termini di database. Non esitate a chiedere chiarimenti se necessario.


Hai preso in considerazione l'utilizzo di un ORM per collegare le classi ai database, come Wt :: Dbo ?
user52875

Risposte:


11

Preferisco il modello di repository per incapsulare l'accesso ai dati. In breve, il repository è responsabile del caricamento di tutti i dati richiesti per un oggetto specifico. Supponi di avere un oggetto Car, come nel tuo esempio. Ma tutti gli attributi per la macchina, marca, modello, anno, proprietari, caratteristiche (lettore CD, 4wd ecc) sono memorizzati in varie tabelle in tutto il database. Il repository determina come caricare e salvare i dati. Se sono necessarie più query più piccole, va bene, ma solo il modello di repository deve saperlo. Il livello di servizio che richiama il repository deve solo sapere quale repository richiamare.

Ciò può quindi essere combinato con l' unità del modello di lavoro . Quindi, nel tuo esempio, il livello di servizio direbbe che deve caricare un'entità auto, ha una sorta di identificatore univoco e invia tale identificativo al repository. Il repository restituisce l'entità auto. Alcuni altri codici manipolano l'entità auto e la rimandano al repository in modo che possa essere salvata.

Se vuoi davvero fare tutto, il livello del repository espone solo le interfacce, come ICarRepository. Il repository conterrebbe una factory che il livello di servizio userebbe per ottenere l'interfaccia ICarRepository. Tutti gli accessi al database sarebbero nascosti dietro un'interfaccia, il che rende molto più semplice il test delle unità.


Tutto bello tranne l'ultimo bit sulle interfacce che in c ++ non esistono (a meno che l'OP non intendesse etichettare c ++). Sono molto curioso di vedere un'implementazione del modello di repository in C ++ poiché voglio usarlo con QT. Sono sorpreso che non ci sia nulla di utilizzabile online = [
johnildergleidisson

6

Ho usato il modello di strategia per incapsulare l'accesso ai dati. Questo modello consente di nascondere il tipo di archiviazione che si sta utilizzando dietro un'interfaccia comune. Nell'interfaccia, definire i metodi di accesso ai dati ignorando il tipo di archiviazione (file, database, web). Quindi, per la tua scelta di archiviazione corrente, in una classe che realizza l'interfaccia di strategia, implementa i dettagli di accesso ai dati. In questo modo la tua applicazione non si preoccupa dell'origine dati che stai utilizzando.

È inoltre possibile creare un livello di servizio che utilizza l'attuale istanza della strategia di archiviazione dei dati per definire dettagli più specifici dell'applicazione invece di mescolare insieme l'accesso ai dati e la logica aziendale.


Quindi, aggiungeresti una classe di accesso per ogni tipo o una grande classe per tutti?
Will03uk,

personalmente considererei anche l'adozione di cast espliciti per ogni dato che proviene dal selvaggio sul mio server / applicazione.
user827992

+1 Mi piace l'aspetto di questo modello, ma ritengo (sulla scala del mio progetto) difficile gestire ciascun algoritmo separatamente per un database; anche se certamente lo userò in altre applicazioni. Lambdas deve completare bene.
Will03uk,

1

Questo è un esempio di modello Factory di database;

using System.Reflection;
using System.Configuration;

public sealed class DatabaseFactory
{
    public static DatabaseFactorySectionHandler sectionHandler = (DatabaseFactorySectionHandler)ConfigurationManager.GetSection("DatabaseFactoryConfiguration");

    private DatabaseFactory() { }

    public static Database CreateDatabase()
    {
        // Verify a DatabaseFactoryConfiguration line exists in the web.config.
        if (sectionHandler.Name.Length == 0)
        {
            throw new Exception("Database name not defined in DatabaseFactoryConfiguration section of web.config.");
        }

        try
        {
            // Find the class
            Type database = Type.GetType(sectionHandler.Name);

            // Get it's constructor
            ConstructorInfo constructor = database.GetConstructor(new Type[] { });

            // Invoke it's constructor, which returns an instance.
            Database createdObject = (Database)constructor.Invoke(null);

            // Initialize the connection string property for the database.
            createdObject.connectionString = sectionHandler.ConnectionString;

            // Pass back the instance as a Database
            return createdObject;
        }
        catch (Exception excep)
        {
            throw new Exception("Error instantiating database " + sectionHandler.Name + ". " + excep.Message);
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.