Perché non dovrei usare il modello di repository con Entity Framework?

190

Durante un colloquio di lavoro, mi è stato chiesto di spiegare perché il modello di repository non è un buon modello per lavorare con ORM come Entity Framework. Perché è così?

    
posta StringBuilder 12.12.2012 - 20:50
fonte

9 risposte

93

Non vedo alcun motivo per cui il modello di repository non funzioni con Entity Framework. Il modello di deposito è un livello di astrazione che si inserisce nel livello di accesso ai dati. Il livello di accesso ai dati può essere qualsiasi cosa, dalle stored procedure ADO.NET allo stato Entity Framework o un file XML.

Nei sistemi di grandi dimensioni, in cui si hanno dati provenienti da fonti diverse (database / XML / servizio web), è bene avere un livello di astrazione. Il modello di repository funziona bene in questo scenario. Non credo che Entity Framework sia abbastanza astratto per nascondere ciò che accade dietro le quinte.

Ho utilizzato il modello di repository con Entity Framework come metodo del livello di accesso ai dati e sto ancora affrontando un problema.

Un altro vantaggio di astrarre il DbContext con un repository è unit testabilità . Puoi avere la tua interfaccia IRepository a cui hanno 2 implementazioni, una (il vero repository) che usa DbContext per parlare al database e la seconda, FakeRepository che può restituire oggetti in memoria / dati derisori. Questo rende il IRepository unit-testabile, quindi altre parti di codice che utilizzano IRepository .

public interface IRepository
{
  IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
  private YourDbContext db;
  private EFRepository()
  {
    db = new YourDbContext();
  }
  public IEnumerable<CustomerDto> GetCustomers()
  {
    return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
  }
}
public MockRepository : IRepository
{
  public IEnumerable<CustomerDto> GetCustomers()
  {
    // to do : return a mock list of Customers
    // Or you may even use a mocking framework like Moq
  }
}

Ora usando DI, ottieni l'implementazione

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}
    
risposta data 12.12.2012 - 22:02
fonte
402

La sola ragione migliore per non utilizzare il modello di repository con Entity Framework? Entity Framework già implementa un pattern di repository. DbContext è il tuo UoW (unità di lavoro) e ogni DbSet è il repository. Implementare un altro livello sopra questo non è solo ridondante, ma rende più difficile la manutenzione.

Le persone seguono i modelli senza rendersi conto dello scopo del modello. Nel caso del modello di repository, lo scopo è di astrarre la logica di query di database di basso livello. Ai vecchi tempi di scrivere effettivamente istruzioni SQL nel tuo codice, il pattern del repository era un modo per spostare quell'SQL dai singoli metodi sparsi per il tuo codice base e localizzarlo in un unico posto. Avere un ORM come Entity Framework, NHibernate, ecc. È una sostituzione per questa astrazione del codice, e come tale, nega la necessità del pattern.

Tuttavia, non è una cattiva idea creare un'astrazione sopra il tuo ORM, ma non qualcosa di così complesso come UoW / Repostitory. Vado con un modello di servizio, in cui si costruisce un'API che l'applicazione può utilizzare senza sapere o preoccuparsi se i dati provengono da Entity Framework, NHibernate o un'API Web. Questo è molto più semplice, semplicemente aggiungendo metodi alla classe di servizio per restituire i dati necessari all'applicazione. Ad esempio, se stavi scrivendo un'app To-do, potresti avere una chiamata di servizio per restituire gli articoli che sono dovuti questa settimana e che non sono ancora stati completati. Tutta la tua app sa che se vuole questa informazione, chiama quel metodo. Dentro quel metodo e nel tuo servizio in generale, interagisci con Entity Framework o qualsiasi altra cosa tu stia utilizzando. Quindi, se in un secondo momento decidi di cambiare ORM o di estrarre le informazioni da un'API Web, devi solo cambiare il servizio e il resto del codice va avanti felicemente, nessuno più saggio.

Potrebbe sembrare che questo sia un potenziale argomento per l'utilizzo del pattern di repository, ma la differenza chiave qui è che un servizio è un livello più sottile e orientato a restituire dati completamente integrati, piuttosto che a qualcosa su cui si continua a eseguire query, come con un repository.

    
risposta data 03.12.2013 - 03:23
fonte
42

Ecco una presa di Ayende Rahien: Architecting in the pit of doom: I malvagi del livello di astrazione del repository

Non sono ancora sicuro se sono d'accordo con la sua conclusione. È un catch-22 - da un lato, se avvolgo il mio EF Context in repository specifici per tipo con metodi di recupero dei dati specifici delle query, sono effettivamente in grado di testare il mio codice (una specie), che è quasi impossibile con Entity Quadro da solo. D'altra parte, perdo la capacità di fare query complesse e manutenzione semantica delle relazioni (ma anche quando ho pieno accesso a quelle funzionalità mi sento sempre come se stessi camminando su gusci d'uovo intorno a EF o qualsiasi altro ORM potrei scegliere , dal momento che non so mai quali metodi la sua implementazione IQueryable possa o non possa supportare, se interpreterà la mia aggiunta a una raccolta di proprietà di navigazione come una creazione o semplicemente un'associazione, se sta per caricare pigro o impaziente o non caricare affatto di default, ecc., quindi forse è meglio così. La "mappatura" relazionale oggettuale a impedenza zero è qualcosa di creatura mitologica - forse è per questo che l'ultima versione di Entity Framework era il nome in codice "Magic Unicorn").

Tuttavia, recuperare le tue entità attraverso metodi di recupero dei dati specifici delle query significa che i tuoi test unitari ora sono essenzialmente test white-box e non hai scelta in questa materia, poiché devi sapere in anticipo esattamente quale metodo di repository l'unità in prova chiamerà per deriderlo. E in realtà non stai ancora testando direttamente le query, a meno che tu non scriva anche test di integrazione.

Questi sono problemi complessi che richiedono una soluzione complessa. Non puoi aggiustarlo fingendo semplicemente che tutte le tue entità siano tipi separati senza relazioni tra loro e ne atomizzino ciascuno nel proprio repository. Beh, puoi , ma fa schifo.

Aggiornamento: ho avuto un certo successo utilizzando il provider Effort per Entity Framework. Lo sforzo è un provider in memoria (open source) che consente di utilizzare EF nei test esattamente come lo si userebbe su un vero database. Sto considerando di passare tutti i test in questo progetto che sto lavorando per utilizzare questo provider, dal momento che sembra rendere le cose molto più semplici. È l'unica soluzione che ho trovato finora che affronta tutti i problemi che stavo cacciando in precedenza. L'unica cosa è che c'è un leggero ritardo all'avvio dei miei test mentre sta creando il database in memoria (usa un altro pacchetto chiamato NMemory per farlo), ma non lo vedo come un problema reale. C'è un articolo Code Project che parla dell'utilizzo di Effort (contro SQL CE) per il test.

    
risposta data 31.12.2012 - 19:17
fonte
16

Il motivo per cui probabilmente lo faresti è perché è un po 'ridondante. Entity Framework ti offre una vasta gamma di vantaggi funzionali e di codifica, ecco perché lo usi, se lo prendi e lo avvolgi in uno schema di repository stai buttando via questi vantaggi, potresti anche utilizzare qualsiasi altro livello di accesso ai dati.

    
risposta data 20.03.2013 - 18:05
fonte
14

In teoria penso che abbia senso incapsulare la logica di connessione al database per renderlo più facilmente riusabile, ma come argomenta il link sottostante, i nostri moderni framework si occupano essenzialmente di questo ora.

Riconsiderazione del modello di deposito

    
risposta data 12.12.2012 - 21:32
fonte
6

Un ottimo motivo per utilizzare il modello di repository è consentire la separazione della logica aziendale e / o dell'interfaccia utente da System.Data.Entity. Ci sono numerosi vantaggi a questo, compresi i reali benefici nei test unitari, consentendo l'utilizzo di Fakes o Mock.

    
risposta data 20.06.2013 - 17:41
fonte
0

Abbiamo avuto problemi con istanze DbContext Entity Framework duplicate ma diverse quando un contenitore IoC che i nuovi repository () su per tipo (ad esempio un repository utente e un'istanza GroupRepository che chiamano il proprio IDbSet da DBContext), a volte possono causare più contesti per richiesta (in un contesto MVC / web).

La maggior parte delle volte funziona ancora, ma quando si aggiunge un livello di servizio sopra e questi servizi assumono che gli oggetti creati con un contesto verranno collegati correttamente come raccolte figlio a un nuovo oggetto in un altro contesto, a volte fallisce e a volte non dipende dalla velocità dei commit.

    
risposta data 04.02.2015 - 21:38
fonte
-1

Dopo aver provato il pattern del repository su un piccolo progetto, consiglio vivamente di non usarlo; non perché complica il tuo sistema, e non perché i dati di derisione sono un incubo, ma perché il tuo test diventa inutile !!

I dati di simulazione consentono di aggiungere dettagli senza intestazioni, aggiungere record che violano i vincoli del database e rimuovere entità che il database rifiuta di rimuovere. Nel mondo reale un singolo aggiornamento può interessare più tabelle, log, cronologia, riepiloghi, ecc., Nonché colonne come il campo data dell'ultima modifica, chiavi generate automaticamente, campi calcolati.

In breve tempo, il test sul database reale ti offre risultati concreti e puoi testare non solo i tuoi servizi e le tue interfacce, ma anche il comportamento del database. Puoi verificare se le tue stored procedure fanno la cosa giusta con i dati, restituire il risultato atteso, o che il record che hai inviato per eliminare veramente cancellato! Tali test possono anche esporre problemi come dimenticare di sollevare errori dalla stored procedure e migliaia di tali scenari.

Penso che il framework di entità implementa il pattern di repository meglio di qualsiasi altro articolo che ho letto finora e va molto al di là di quello che stanno cercando di realizzare.

Il repository era la migliore pratica in quei giorni in cui stavamo usando XBase, AdoX e Ado.Net, ma con entità !! (Repository over repository)

Infine penso che troppe persone investano molto tempo nell'apprendimento e nell'implementazione di schemi di deposito e si rifiutano di lasciar perdere. Soprattutto per dimostrare a se stessi che non hanno perso il loro tempo.

    
risposta data 15.10.2015 - 04:40
fonte
-3

È dovuto alle migrazioni: non è possibile far funzionare le migrazioni, poiché la stringa di connessione risiede nel web.config. Ma, DbContext risiede nel livello del repository. IDbContextFactory deve avere una stringa di configurazione nel database. Ma non c'è modo che le migrazioni ottengano la stringa di connessione da web.config.

Ci sono dei problemi ma non ho ancora trovato una soluzione pulita per questo!

    
risposta data 18.12.2015 - 06:16
fonte

Leggi altre domande sui tag