Repository generico Con EF 4.1 qual è il punto


145

Mentre approfondisco DbContext, DbSet e le interfacce associate, mi chiedo perché avresti bisogno di implementare un repository "generico" separato attorno a queste implementazioni?

Sembra che DbContext e IDbSet facciano tutto il necessario e includano "Unit Of Work" all'interno di DbContext.

Mi sto perdendo qualcosa qui o sembra che la gente si diverta ad aggiungere un altro livello di dipendenza senza motivo.


Questo è un problema controverso / basato sull'opinione. Ne ho discusso qui .
Amit Joshi,

Risposte:


202

In realtà hai ragione. DbContextè un'implementazione dell'unità del modello di lavoro ed IDbSetè un'implementazione del modello di repository.

I repository sono attualmente molto popolari e abusati. Tutti li usano solo perché ci sono dozzine di articoli sulla creazione di repository per il framework di entità, ma nessuno in realtà descrive le sfide legate a questa decisione.

I motivi principali per l'utilizzo del repository sono in genere:

  • Nascondi EF dallo strato superiore
  • Rendi il codice più facilmente testabile

Il primo motivo è una sorta di purezza architettonica e un'ottima idea che se rendi indipendenti i tuoi strati superiori su EF, puoi in seguito passare ad un altro schema di persistenza. Quante volte hai visto cose del genere nel mondo reale? Questo motivo rende molto più difficile lavorare con EF perché il tuo repository deve esporre molte funzionalità aggiuntive che avvolgono ciò che EF consente di default.

Allo stesso tempo, il wrapping del codice EF consente di organizzare meglio il codice e seguire la regola Separazione delle preoccupazioni. Per me questo può essere l'unico vero vantaggio del repository e dell'unità di lavoro, ma devi capire che seguire questa regola con EF renderà forse il tuo codice più facilmente gestibile e leggibile, ma nello sforzo iniziale di creare la tua applicazione sarà molto più elevato e per applicazioni più piccole questa può essere una complessità non necessaria.

Il secondo motivo è parzialmente corretto. Il grande svantaggio di EF è l'architettura rigida che può essere difficilmente presa in giro, quindi se si desidera testare l'unità di livello superiore, è necessario avvolgere EF in qualche modo per consentire di deridere la sua implementazione. Ma questo ha molte altre conseguenze che ho descritto qui .

Seguo il blog di Ayende . Se hai mai usato NHibernate probabilmente conosci i suoi articoli. Questo ragazzo ha recentemente scritto diversi articoli contro l'utilizzo del repository con NHibernate ma NHibernate è molto meglio deridibile.


3
Puoi deridere IDbSetpuoi anche definire un'interfaccia personalizzata nel tuo contesto derivato, ma questo è tutto. Una volta che il codice utilizza ChangeTracker, voci o qualsiasi altra cosa, sarà necessario un grande sforzo per racchiuderli tutti.
Ladislav Mrnka,

1
Sì, EF non è uno strumento molto orientato alle prestazioni. Almeno MS ha molte opportunità di migliorarlo nelle versioni future.
Ladislav Mrnka,

2
@chiccodoro: giusto. Ma una volta che la tua classe derisa espone IQueryableo accetta Expression<>come parametro che viene messo internamente alla query Linq-a-entità, stai definendo la logica al di fuori del componente deriso con effetti collaterali che non possono essere testati con unit test.
Ladislav Mrnka,

8
Se sto usando DbSet e BdContext direttamente nel mio livello aziendale, devo fare riferimento a EntityFramework.dll e al mio progetto DataLayer. Solo questo mi dice che ha bisogno di una sorta di avvolgimento.
Ingó Vals,

2
downvote: incompleto: astrarre EF dietro un'interfaccia di repository può far funzionare lo stesso codice client esatto sia in SL che in WPF.
h.alex,

21

Sto lottando con gli stessi problemi e la derisione per i test unitari degli strati EF è importante. Ma mi sono imbattuto in questo fantastico articolo che spiega come impostare il DbContext di EF 4.1 in modo da renderlo beffardo assicurandomi che il tuo DbContext derivato implementasse un'interfaccia generica ed esponesse IDbSet anziché DbSet. Dal momento che sto usando un approccio Database First, poiché il nostro database esiste già, ho semplicemente modificato i modelli T4 utilizzati per generare il mio DbContext derivato per generarlo per restituire interfacce IDbSet, nonché derivare dalla mia interfaccia generica. In questo modo l'intera cosa può essere facilmente derisa e non è necessario implementare la propria unità di lavoro o modello di repository. Scrivi il tuo codice di servizio per utilizzare l'interfaccia generica e quando vai all'unità testalo,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/


5

Un motivo per la creazione del repository è che puoi nascondere l'implementazione di DBSet e DbContext se decidi di passare da EntityFramework a qualcos'altro o viceversa.

Ad esempio, stavo usando NHibernate e ho racchiuso tutte le chiamate a quel framework all'interno delle mie classi di repository. Restituiscono IEnumerable perché diventano "generici" e i miei repository hanno le operazioni CRUD standard (aggiornamento, eliminazione, ecc.). Da tempo mi sono trasferito in Entity Framework. Nel fare ciò, non avevo bisogno di cambiare nulla nelle mie classi ViewModel o oltre perché indicavano il mio repository - avevo solo bisogno di cambiare l'interno del mio repository. Ciò ha reso la vita molto più semplice durante la migrazione.

(Ho usato NHibernate perché ci stiamo collegando a ISeries e, al momento, non c'erano implementazioni a basso costo usando EF con ISeries. L'unico disponibile era pagare $ 12.000 a IBM per il loro DB2Connect)


"Quasi" (in materia di nascondere DBSet e DbContext) scoprirai che non è necessario esporre EF a nessun consumatore (ad esempio se si utilizza DI) ma è necessaria un'interfaccia che esponga le proprietà IDbSet <T> o fare un passo ulteriore e invece digitare tutte le proprietà come IQueryable <T>, ma il punto è che puoi nascondere completamente la tua dipendenza da DbSet e DbContext. Le operazioni CRUD possono quindi essere scritte come metodi di estensione, è possibile scrivere più metodi di estensione per diversi negozi di supporto. Tuttavia, non nasconderesti l'uso di LINQ.
Shaun Wilson,
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.