11

Attualmente sto usando EF e sto utilizzando il suo datacontext direttamente in tutte le mie azioni, ma da quando ho iniziato a leggere sull'accoppiamento e sulla testabilità mi sto pensando che questo non sia il modo migliore per andare. Sto cercando di capire tutti i pro ei contro prima di iniziare il refactoring su tutto il mio codice attuale.ASP.NET MVC utilizzando il modello di repository

Problema 1: Considerando che ogni entità ha bisogno di un proprio repository, e quindi ha di impostare la propria connessione a un'origine dati (lascia supporre un database utilizzando EF), wouldnt che danno un sacco di spese generali, se ho bisogno di dati da 5 diverse entità su una singola pagina?

Problema 2: Cosa im vedendo partecipavano in tutti gli esempi che ho trovato on-line è che la maggior parte delle persone (anche gente come shanselman) implementare il modello di repository utilizzando le classi di entità che vengono generati da una LINQ o EF, doesn questo sconfigge lo scopo del modello di deposito per quanto riguarda l'accoppiamento libero? D'altra parte, qual è l'alternativa, usando le classi POCO in combinazione con ad esempio AutoMapper? (questo mi spaventa un po ')

Im sperando che alcune persone possano far luce su questo, perché sono un po' confuso al momento se il modello di repository è la scelta giusta per un sito web.

risposta

3

ObjectContext utilizza il pool di connessioni, quindi non sarà così inefficiente come si potrebbe pensare. Inoltre, i server SQL (ad esempio MSSQL) sono ottimizzati per tonnellate di connessioni simultanee.

Per quanto riguarda come implementarlo, vorrei andare con qualche interfaccia IRepository. È quindi possibile creare interfacce specifiche, ovvero PostRepository> IRepository e, infine, implementarle in classi concrete (ad esempio, una classe reale e una falsa in memoria per testare).

+0

Sono consapevole del fatto che i server SQL utilizzano il pool di connessioni, ma la mia preoccupazione principale è il modo in cui EF o LINQ mantengono il proprio contesto, sembra che ci sia ancora molto da fare sott'acqua e che 5 volte (o più) equivalga a un sovraccarico o sono completamente di base qui? – Fabian

+0

È stato progettato esattamente per evitare tali sovraccarichi. Generalmente puoi creare molti oggetti ObjectContext in un colpo solo senza problemi. Ma non prenderlo nel modo sbagliato; non dovresti essere negligente. –

+0

EF/LINQ utilizzano ADO.NET sotto il cofano, quindi finiscono per utilizzare gli stessi meccanismi di pooling delle connessioni. –

1

Il pool di connessioni ADO.NET gestirà le connessioni dietro le quinte. Fondamentalmente non importa affatto quante diverse entità (e quindi i Repository con il proprio contesto) si utilizzano; ogni operazione DB prenderà le connessioni dallo stesso pool.

Il motivo del repository è di consentire di astrarre/sostituire il modo in cui le entità vengono create per il test, ecc. Gli oggetti entità possono essere istanziati come normali oggetti senza i servizi del contesto, quindi il repository di test lo farebbe per i dati di test

+1

Nota aggiuntiva sul pool di connessioni: si presume che tutte le connessioni utilizzino la stessa stringa di connessione. Ogni singola stringa di connessione che è possibile utilizzare (anche se l'unica differenza è uno spazio aggiunto) ottiene il proprio pool di oggetti di connessione. –

+0

Quindi non importa se utilizzo un singolo datacontext o 10 se si guarda alle prestazioni? – Fabian

+0

Per quanto riguarda le connessioni, nessuna differenza misurabile. Tutto ciò presuppone anche che nessuno abbia aggiunto una serie di codice "pesante" al datacontext o ai repository. –

3

In primo luogo, non sono a conoscenza di un requisito per ogni entità di avere il proprio repository quindi avrei spazzato quella restrizione.

Per l'implementazione di Scott H, presumo che ti riferisci all'app Nerd Dinner, che per sua stessa ammissione non è realmente il modello di repository.

L'obiettivo del modello di repository è, come si immagina, isolare l'archivio dati dagli strati sopra di esso. Non è puramente per motivi di test, ma consente anche di modificare il backing store senza influire sull'interfaccia utente/business logic.

In termini puristi, si creerebbero POCO che si restituirebbero dal repository al BL, utilizzando un'interfaccia per definire il contratto di repository da poter passare e utilizzare l'interfaccia anziché un'implementazione concreta. Ciò consentirebbe di passare in qualsiasi oggetto che ha implementato l'interfaccia del repository, sia che si tratti del tuo repository live o di un repository mocked.

In realtà io uso un repository con MVC con Linq to SQL come il mio backing store che mi consente una certa flessibilità sull'attuale backing store, quindi uso oggetti L2S fatti a mano nel mio BL, questi hanno campi e funzionalità aggiuntivi che non è persistente nel backing store. In questo modo ottengo alcune grandi funzionalità dagli aspetti L2S, il rilevamento delle modifiche, la gerarchia degli oggetti, ecc. E mi consente anche di sostituire un repository fittizio per TDD.

+0

Penso che sto diventando uno di quei puristi, l'automapper è l'unico modo per creare un'istanza se voglio evitare un sacco di codici a sinistra per la mano destra? E gli oggetti L2S fatti a mano mi intrigano, questo è come POCO ma non proprio? – Fabian

+0

Puoi usare l'automapper e quindi estendere le classi (credo che EF come L2S crei partial) ma EF porta con sé molti altri problemi, non ho avuto bisogno di usarlo ancora e conosco almeno una persona che vuole scaricarlo. Gli oggetti L2S realizzati a mano sono effettivamente POCO ma decorati utilizzando gli attributi Linq To SQL e accessibili tramite un contesto di dati istanziati dal repository. – Lazarus

2

Hai colpito il chiodo sulla testa identificando la difficoltà con l'utilizzo di Entità come oggetti di business. Dopo molte prove ed errori, ecco il modello in cui ci siamo sistemati, che ha funzionato molto bene per noi:

La nostra applicazione è divisa in moduli, e ogni modulo è diviso in tre livelli: Web (front-end), Core (attività) e dati. Nel nostro caso, a ciascuno di questi livelli viene dato un proprio progetto, quindi c'è una dura applicazione che impedisce alle nostre dipendenze di diventare strettamente accoppiate.

Il livello Core contiene classi di utilità, POCO e interfacce repository.

Il livello Web utilizza queste classi e interfacce per ottenere le informazioni necessarie. Ad esempio, un controller MVC può assumere una particolare interfaccia di repository come argomento del costruttore, quindi il nostro framework IoC inietta l'implementazione corretta di quel repository quando viene creato il controller. L'interfaccia del repository definisce i metodi selettori che restituiscono i nostri oggetti POCO (definiti anche nel livello aziendale Core).

Dati L'intera responsabilità del livello è quella di implementare le interfacce del repository definite nel livello Core. Ha un contesto Entity Framework che rappresenta il nostro archivio dati, ma piuttosto che restituire le Entità (che sono tecnicamente oggetti "dati"), restituisce i POCO definiti nel livello Core (i nostri oggetti "business").

Per ridurre la ripetizione, abbiamo una classe astratta, generica EntityMapper, che fornisce funzionalità di base per la mappatura di Entità su POCO. Ciò rende la maggior parte delle implementazioni del nostro repository estremamente semplice. Per esempio:

public class EditLayoutChannelEntMapper : EntityMapper<Entity.LayoutChannel, EditLayoutChannel>, 
    IEditLayoutChannelRepository 
{ 
    protected override System.Linq.Expressions.Expression<Func<Entity.LayoutChannel, EditLayoutChannel>> Selector 
    { 
     get 
     { 
      return lc => new EditLayoutChannel 
          { 
           LayoutChannelId = lc.LayoutChannelId, 
           LayoutDisplayColumnId = lc.LayoutDisplayColId, 
           ChannelKey = lc.PortalChannelKey, 
           SortOrder = lc.Priority 
          }; 
     } 
    } 
    public EditLayoutChannel GetById(int layoutChannelId) 
    { 
     return SelectSingle(c => c.LayoutChannelId == layoutChannelId); 
    } 
} 

Grazie ai metodi implementati dalla classe base EntityMapper, il repository sopra implementa la seguente interfaccia:

public interface IEditLayoutChannelRepository 
{ 
    EditLayoutChannel GetById(int layoutChannelId); 
    void Update(EditLayoutChannel editLayoutChannel); 
    int Insert(EditLayoutChannel editLayoutChannel); 
    void Delete(EditLayoutChannel layoutChannel); 
} 

EntityMappers fare molto poco in loro costruttori, quindi è bene se un controller ha più dipendenze del repository. Entity Framework non solo riutilizza le connessioni, ma i contesti di entità vengono creati solo quando viene richiamato uno dei metodi del repository.

Ogni modulo ha anche un progetto speciale Test, che contiene test di unità per le classi in questi tre livelli. Abbiamo persino escogitato un modo per rendere i nostri repository e altre classi di accesso ai dati in qualche modo testabili da unità. Ora che abbiamo configurato questa infrastruttura di base, l'aggiunta di funzionalità alla nostra applicazione Web è in genere piuttosto semplice e non troppo soggetta a errori.

2

Problema 2: un modo per evitare questo sarebbe utilizzare qualcosa come "ADO.NET C# POCO Entity Generator".

+0

Sto usando il generatore di entità POCO ADO.NET C# in questo momento con Entity Framework 4.1 e Structure Map come nostra iniezione di dipendenza. Stiamo anche utilizzando MvcScaffolding per generare controller, repository e interfacce basati su pocos. Le cose stanno andando abbastanza bene finora, i test case e il mocking sono stati abbastanza facili da fare, e i pocos generati sono puliti e facili da usare. Consiglierei di esaminare questo strumento. – Chris

Problemi correlati