Domanda

Ho una situazione molto comune qui. E per anni non ho trovato se quello che sto facendo è GIUSTO secondo gli standard del settore. Considera un'applicazione che si collega al database, ma in cui la stringa di connessione invece di essere memorizzata in alcuni file / impostazioni viene passata come parametro della riga di comando all'avvio OPPURE il database viene sfogliato al momento dell'avvio dell'applicazione.

Bene, diventa necessario salvare quella stringa di connessione da qualche parte all'interno dell'ambito dell'app. Il modo più comune che ho visto è un modulo o una classe globale con metodi get / set per salvare la stringa di connessioni. Un altro modo in cui lo farei è usare Singleton. Entrambe le opzioni il mio DAL può accedere alla stringa di connessione quando è necessario tramite un metodo GetConnectionString.

Esiste un modo MIGLIORE per farlo?

Aggiornamento: non ho un file di configurazione e anche se lo avessi fatto avrei bisogno di leggere una volta la stringa di connessione per tutta la vita dell'istanza dell'applicazione. puoi elaborare il " iniettarlo in qualsiasi classe " parte

È stato utile?

Soluzione

In generale stato globale, sia esso una classe globale o un singleton, dovrebbe essere evitato ovunque possibile.

La soluzione ideale sarebbe che la tua applicazione carichi la stringa di connessione fuori da config e inject in tutte le classi che ne hanno bisogno. A seconda delle dimensioni dell'applicazione, un contenitore IoC come Unity o Castle Windsor può essere d'aiuto, ma certamente non è una parte richiesta della soluzione.

Se questa non è un'opzione e sei bloccato a mantenere lo stato globale (a causa della base di codice esistente o altro), non so che c'è un'enorme differenza tra i due approcci che hai suggerito.

Aggiornamento: Giusto per chiarire, dimentica tutto ciò che riguarda i contenitori IoC per ora, " Inject " è solo un modo elegante per dire " passare come parametro " (o al costruttore della classe, o tramite una proprietà o altro).

Anziché disporre di una classe di accesso ai dati, è necessario richiedere la stringa di connessione (da una sorta di globale o singleton), ma deve essere passata tramite il costruttore o una proprietà.

Aggiornamento n. 2: penso che ci siano ancora alcuni malintesi su ciò che comporta questo approccio.

Fondamentalmente dipende dal fatto che la tua classe di accesso ai dati assomigli a:

public class DataAccessClass
{
    public DataAccessClass()
    {
        _connString = SomeStaticThing.GetConnectionString();
    }
}

o

public class DataAccessClass
{
    public DataAccessClass(string connString)
    {
        _connString = connString;
    }
}

Questi articoli (e in effetti, molti degli articoli di quel blog ) espone in dettaglio una serie di motivi per cui il secondo è migliore del primo (non ultimo perché il primo è quasi impossibile da testare l'unità).

Sì, in da qualche parte ci dovrà essere un ragazzo statico responsabile per afferrare la stringa di connessione in primo luogo, ma il punto è che le tue dipendenze dai metodi statici sono limitate a quello un punto (che probabilmente sarà il tuo metodo principale nel processo di bootstrap della tua applicazione), piuttosto che spruzzato su tutta la tua base di codice.

Altri suggerimenti

è bello vedere così tante soluzioni creative per un problema così semplice ;-)

fatti salienti, dalla domanda OP:

  1. la stringa di connessione viene passata sulla riga di comando
  2. potrebbe essere necessario che molti altri componenti utilizzino la stringa di connessione

quindi, non c'è modo di aggirare l'uso di un elemento statico ; non importa se si tratta di una variabile globale (difficile da eseguire in .NET, davvero, senza una classe chiusa), una classe statica o un singleton. La soluzione del percorso più breve sarebbe una classe statica inizializzata dalla classe Program che ha elaborato la riga di comando.

Ogni altra soluzione richiederebbe comunque l'accesso statico alla stringa di connessione passata , anche se potrebbe nasconderlo dietro uno o più livelli di riferimento indiretto.

Non sto dicendo che non vuoi vestire la soluzione di base con qualcosa di più elaborato, ma non è necessario e non elimina la natura fondamentalmente statica / globale della stringa di connessione come descritto .

Se tutto ciò che stai memorizzando è una stringa (possibilmente insieme a un gruppo di altre impostazioni globali dell'applicazione), userei semplicemente una classe statica con un gruppo di proprietà per contenere tutto ciò. Un singleton è più codice & Amp; lavori di manutenzione, ma in questo caso non è necessario, poiché la tua classe di proprietà non farà nulla; tieni semplicemente le cose.

Sicuro che c'è un modo. Prima di tutto, tieniti aggiornato su iniezione di dipendenza e tecniche simili e leggi livello di servizio poiché è quello che ti serve qui.

Per quanto riguarda un livello di servizio, è necessario un qualche tipo di servizio di configurazione, che sarà il modulo su cui verranno interrogate parti dell'applicazione per informazioni sulla configurazione: stringhe di connessione, URI, ecc .:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

Dovrebbe essere abbastanza semplice che ci sia solo un'istanza di IConfigurationService nel tuo sistema, ma questo si ottiene con un contenitore DI di scelta, non con il modello Singleton.

Successivamente, tutti i tuoi servizi DAL riceveranno un riferimento a IConfigurationService.ConnectionString:

class FooDal : IFooDal
    IConfigurationService ConfigurationService

in modo che ora possano utilizzare <=> per ottenere la stringa di connessione.

Dipende.

Tieni presente che un singleton:

  • impone che esista esattamente un'istanza della classe e
  • fornisce accesso globale

Un globale fornisce solo accesso globale, ma non fornisce garanzie sul numero di istanze.

E l'opzione finale, ovviamente, è usare una variabile locale. Cioè, passa le informazioni di configurazione come parametro al costruttore o ovunque sia necessario.

Scegliere tra i primi due dovrebbe essere piuttosto semplice. È un disastro se esistono due istanze della classe? Direi che probabilmente no. Potrei voler creare un'istanza della mia classe di configurazione per fornire opzioni separate a un componente specifico dell'app o per inizializzare i test delle unità.

Ci sono pochissimi casi in cui hai bisogno di per garantire che esista esattamente un'istanza. Per questo motivo, un globale può essere una scelta migliore di un singleton.

La scelta tra locale e globale è più complicata. Lo stato mutabile globale è generalmente meglio evitare. Lo stato immutabile è meno problematico e non porterà agli stessi problemi di sincronizzazione / concorrenza / scalabilità che lo stato mutabile globale farà

Ma spesso, può essere preferibile averlo come variabile locale da passare ai componenti che ne hanno bisogno. Questo può essere fatto manualmente, semplicemente passandolo al costruttore di un oggetto che necessita dell'accesso al DB, o in una certa misura può essere automatizzato con un contenitore IoC.

Ma in ogni caso, se non è globale, il tuo codice diventa più generico e più portatile. Se ha dipendenze nascoste da classi e dati globali che deve esistono per far funzionare il codice, allora non può essere facilmente utilizzato in altri progetti o se si refacturing troppo l'intera base di codice.

Diventa anche più difficile test unitario, ancora una volta perché il codice non verrà nemmeno compilato a meno che non esistano alcuni dati esterni che vorremmo idealmente lasciare fuori dal nostro test.

come ha detto @Anton, devi avere un'interfaccia che esponga qualcosa di simile

interface IConfigurationService
    string ConnectionString

Quindi ogni volta che una tua classe necessita della tua stringa di connessione, fornisci un'implementazione di IConfigurationService su costruzione contenente la stringa valida. Devi trovare un posto valido per creare l'implementazione all'avvio dell'app (probabilmente l'ora in cui otterrai l'indirizzo del database) e passarlo alla classe che ne ha bisogno.

Anche se potrebbe sembrare molto lavoro rispetto a singleton o global, questo ridurrà l'accoppiamento nel tuo codice migliorando quindi la riusabilità e renderà più semplice il test delle unità, ed è piuttosto semplice una volta che ti convinci che i globali sono (principalmente) cattivi: )

Come accennato in precedenza, ci sono contenitori IoC che forniranno il framework per farlo, ma potrebbe essere eccessivo nel tuo caso, oltre a essere bello usare il modello per te stesso prima di lasciare che il lavoro sia " magic scatola quot &;

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top