Iniezione delle dipendenze con soluzione Entity Framework di livello n


12

Attualmente sto progettando una soluzione di livello n che utilizza Entity Framework 5 (.net 4) come strategia di accesso ai dati, ma sono preoccupato su come incorporare l'iniezione di dipendenza per renderla verificabile / flessibile.

Il layout della mia soluzione attuale è il seguente (la mia soluzione si chiama Alcatraz):

Alcatraz.WebUI : un progetto webform asp.net, l'interfaccia utente front-end, fa riferimento ai progetti Alcatraz.Business e Alcatraz.Data.Models .

Alcatraz.Business : un progetto di biblioteca di classe, contiene la logica aziendale, fa riferimento a progetti Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : un progetto di biblioteca di classe, ospita AlcatrazModel.edmx e AlcatrazEntitiesDbContext, fa riferimento a progetti Alcatraz.Data.Models .

Alcatraz.Data.Models : un progetto di biblioteca di classe, contiene POCO per il modello Alcatraz, nessun riferimento.

La mia visione di come funzionerebbe questa soluzione è che il web-ui avrebbe un'istanza di un repository all'interno della biblioteca aziendale, questo repository avrebbe una dipendenza (attraverso il costruttore) di una stringa di connessione (non AlcatrazEntitiesun'istanza). Il web-ui conosceva le stringhe di connessione al database, ma non che si trattasse di una stringa di connessione del framework di entità.

Nel progetto aziendale:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

Nell'interfaccia utente Web:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

Ho alcune domande correlate su questo disegno.

  1. Questo design ha senso anche in termini di funzionalità di Entity Frameworks? Ho sentito che il framework Entity utilizza già il modello Unit-of-work, sto semplicemente aggiungendo un altro livello di abstract inutilmente?

  2. Non voglio che il mio web-ui comunichi direttamente con Entity Framework (o addirittura lo faccia riferimento a tale proposito), voglio che tutti gli accessi al database passino attraverso il livello aziendale poiché in futuro avrò più progetti utilizzando lo stesso livello aziendale (servizio web, applicazione Windows, ecc.) e voglio che sia facile da mantenere / aggiornare avendo la logica di business in un'area centrale. È un modo appropriato per raggiungere questo obiettivo?

  3. Il livello aziendale dovrebbe contenere anche repository o dovrebbe essere contenuto nel livello Access? Se dove si trovano va bene, passare una stringa di connessione è una buona dipendenza da assumere?

Grazie per aver dedicato del tempo a leggere!

Risposte:


11

Il modo in cui stai facendo DI è sbagliato.

Innanzitutto, la stringa di connessione appartiene al livello dati. O nel file web.config.

La prossima astrazione che dovrai affrontare è il DbContext, non una stringa di connessione. I repository non dovrebbero conoscere le stringhe di connessione. La tua logica aziendale non saprà di DbContext ecc.

La tua UI non ne avrà idea e non creerà istanze per nulla relative a EF.

Risposte concrete ai tuoi punti:

  1. Non aggiungere astrazioni, fino a quando non avrai familiarità con EF. Aggiunge già buone astrazioni come UoW, query, utilizzo di POCO ecc.

  2. Perché DI funzioni, hai una radice di composizione che fa riferimento a tutti i componenti necessari. Questo potrebbe essere o meno nel progetto WebUI. In caso contrario, dovresti aspettarti che non faccia riferimento a EF o qualsiasi altra tecnologia relativa ai dati.

  3. Basta qui. Smetti di aggiungere astrazioni rispetto alle astrazioni. Inizia con un'architettura diretta e "ingenua" e sviluppala nel tempo.

Le astrazioni sono uno strumento per affrontare la complessità. L'assenza di complessità significa che non sono necessarie (ancora) astrazioni.


Per essere chiari, ho capito cosa stai dicendo: il repository (che l'interfaccia esiste nel mondo degli affari e che esiste in Alcatraz.Data.Access?) Accetta un DbContextcome sua dipendenza. Le business class hanno repository come dipendenza. Per l'iniezione di dipendenza, lo sto facendo manualmente (quindi capisco cosa sta succedendo). Il motivo per cui voglio essere in grado di impostare la stringa di connessione DbContextè l'uso del database sharding, quindi in alcuni casi ho bisogno di avere un framework di entità per connettermi a database diversi (della stessa struttura). Ti capisco bene?
Matteo

Dal codice che hai fornito, sembra che tu non stia facendo affatto DI. L'obiettivo principale di DI è liberare te e il tuo codice dalla gestione delle dipendenze. Non riesco a immaginare che lo fai efficacemente manualmente senza un contenitore DI.
Boris Yankov,

inoltre tieni la mente aperta con DI. ho già, per divertimento, fatto esattamente la stessa domanda qui poi su altri forum per ottenere risposte opposte. DI è uno schema, non un'architettura. A seconda del tuo obiettivo puoi decidere di usarlo o meno. Lo uso ma non per i motivi che molte persone mi dicono di usare.
Bastien Vandamme,

4

Qualche breve commento. Personalmente probabilmente non avrei passato una stringa di connessione. Semmai proverei a creare interfacce forse per i repository e passerei semplicemente le interfacce? Chiedi ai repository di implementare o esporre un'interfaccia IOW.

In questo modo non è necessario che sia un database che implementa i tuoi repository. potrebbero essere in una cache di memoria o altro. Forse allora potresti usare una sorta di framework di iniezione di dipendenza per istanziare anche questi?

Quindi, in risposta ad alcune delle tue domande:

  1. Sì, penso che sia ok
  2. Avrei ancora l'interfaccia utente di riferimento al progetto EF e le interfacce di riferimento del livello aziendale implementate dal livello di repository EF. In questo modo altri progetti potrebbero ancora utilizzare gli stessi assiemi ma hanno la flessibilità di scambiarsi se lo si desidera?
  3. hmmm, probabilmente i repository nel livello di accesso ma implementando una definizione di interfaccia esposta nel livello aziendale ??

Questi sono solo alcuni pensieri su cui riflettere.


A proposito del punto 2, un obiettivo che stavo cercando di fare è non avere CRUD direttamente all'interno del livello dell'interfaccia utente. Voglio dire che voglio assicurarmi che solo il CRUD possa avvenire attraversando il livello aziendale, in questo modo è gestito.
Matteo
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.