Qual è la migliore pratica quando si mette un livello di astrazione su un ORM?

7

Quando si lavora con un ORM come Entity Framework, sono caduto in un'abitudine constrongvole. Crea un'interfaccia con i metodi get / add su di essa, metti questo su una classe "Repository" quindi aggiungi un costruttore per ogni classe che accetta l'interfaccia. Utilizza i metodi dell'interfaccia nella classe.

Sembra il modo più semplice per ottenere uno strato di astrazione che poi possa prendere in giro per i test unitari.

Tuttavia, facendo un po 'di lettura sull'argomento, sembra che ci sia molta confusione su quale sia l'approccio migliore. Alcuni esempi includono:

  • Usare il tuo ORM con il pattern di repository (sembra una cattiva idea, dato che la maggior parte degli ORM sono già i propri pattern di repository).

  • Creare un repository riutilizzabile usando i generici e condividerlo attraverso i progetti (sembra un eccesso se non stai lavorando con una grande quantità di progetti diversi).

  • Avere un repository che include una logica di dati più dettagliata. Cioè con metodi come "GetUsersWhereAdminIsTrue" piuttosto che i più generici "GetUsers" (questo sembrerebbe portare a un repository con una lista massiccia di metodi).

  • Utilizzare lo IOC per consentire il test dell'unità del codice senza una fonte esterna senza la necessità di un ulteriore layer wrapper (ho visto questo accennato più volte, ma non lo capisco. Sicuramente anche con IOC si è ancora bisogno di iniettare un'interfaccia?)

La risposta potrebbe essere in parte circostanziata. Ma mi piacerebbe capire perché le persone propongono soluzioni nonostante le carenze che ho messo tra parentesi sopra?

    
posta Matt Thrower 07.09.2015 - 11:28
fonte

4 risposte

2

Forse potresti aggiungere del codice per illustrare i diversi metodi?

Gli ORM sono grandi in quanto forniscono un facile accesso a un DB senza dover digitare un carico di codice piastra e SQL, ma causano problemi se si è pigri e non li si avvolge in una classe di repository.

A. non puoi iniettare una simulazione per testare

B. crei un accoppiamento stretto con le tue classi ORM

C. non puoi controllare il modo in cui viene utilizzato l'ORM, ad es. rilascia le tabelle, esegui una query a basso rendimento ecc.

i punti che descrivi sono i modi in cui le persone hanno cercato di aggirare questi problemi

1: nasconde le tue classi ORM dal resto del progetto e consente il mocking

2: Sono d'accordo con te su questo, ma riduce il numero di metodi in 3

3: ti dà il controllo su query specifiche eseguite e limita l'ambito dell'oggetto

4: Immagino tu voglia usare un framework IoC come una sorta di repo? suona male per me, ma ho visto alcune cose pazzesche fatte con i framework IoC

    
risposta data 10.09.2015 - 23:43
fonte
1

Ho usato l'approccio del repository generico con un factory / provider di repository che ho ricevuto da una classe di valutazione multipla di John Papa (utilizzato anche in questo articolo ). All'inizio sembrava un po 'eccessivo (e non ero nemmeno a conoscenza del fatto che EF avesse usato lo schema dei pronti contro termine a quel punto), ma era molto facile lavorare con il resto del team.

Alcuni vantaggi che ho visto:

  • Separazione dei livelli di astrazione - Ho mantenuto il mio accesso db all'interno di questi repository e mantenuto l'utilizzo del repository nel livello di servizio. Ci sono chiaramente altri modi per ottenere questo risultato, ma questo modello sembrava una guida per farlo.
  • I test unitari sono facili da implementare con la quantità di iniezione in corso
  • Il factory è stato utile solo per creare repository di cui avevo bisogno per l'attività specifica
  • Un numero così elevato di oggetti del modello sono solo elementi di ricerca semplici che utilizzano un repository generico così naturale. È possibile aggiungere più complessità se si crea un repository personalizzato, incluso anche nell'articolo.
  • Pochissime modifiche (e ben documentate) per apportare modifiche al modello

L'unico inconveniente che non è specifico di EF è che i dbset per ciascuna entità sono esposti pubblicamente nel DBContext per poter inizializzare il DB nell'inizializzatore. Ciò significa che devi assicurarti che tutti utilizzino i repository per accedere ai dati, invece di prendere semplicemente un dbset.

    
risposta data 10.09.2015 - 23:12
fonte
1

Sono stato esposto a molti progetti diversi in passato, adottando approcci diversi. Quello che mi è piaciuto di più è il riutilizzo di un'interfaccia dall'ORM da utilizzare in tutto il codice. Nel caso del framework di entità normalmente utilizziamo un DbContext con un gruppo di DbSet & lt ; T > . Fortunatamente DbSet implementa IDbSet < T > che esiste in implementazioni di memoria di .

Quindi possiamo definire un'interfaccia e dipendere da quella in tutto il codice:

interface IFooContext
{
    IDbSet< Bar> Bars{get;}
    IDbSet< Baz> Baz{get;}
}

Questo è un approccio molto leggero perché non dobbiamo scrivere i wrapper. Sono consapevole che non tutti gli ORM hanno un'interfaccia che possiamo usare in questo modo, ma per quelli che è un approccio facile.

    
risposta data 11.09.2015 - 07:19
fonte
1

Attualmente, dove lavoro, usiamo un repository che si trova in cima a EntityFramework in modo che possiamo deriderlo con relativa facilità. Non è abbastanza un repository come nel Pattern del repository, tuttavia agisce come un passaggio tra gli oggetti che usano quel pattern come EntityFramework.

La parte importante qui è che non ha equivalenti a "GetUsers" e "GetUsersWhereAdminIsTrue", invece, usiamo un metodo generico che prende un oggetto generico che costruisce il proprio IQueryable per quello che chiamiamo Criteria. Ciò significa che questo passaggio consiste nei seguenti metodi generici, in cui il tipo è l'oggetto EntityFramework:

  • Aggiungi (prende un oggetto Utenti e lo aggiunge alla tabella Utenti)
  • Rimuovi (prende un oggetto Users e lo rimuove dalla tabella Users)
  • Corrisponde (prende un oggetto Criteri generico di Utenti e restituisce un insieme di Utenti che corrispondono a tali criteri)
  • Salva (non prende alcun parametro, ma salva eventuali modifiche in sospeso)

Una delle parti utili del metodo Corrispondenze è che finalizza la raccolta, che salta alcune preoccupazioni quando si tratta di n + 1 problemi ORM.

Quando si tratta dei nostri attuali oggetti repository, il nostro equivalente a un UsersRepository può avere i metodi GetUsers e GetAdmins, ma tutto ciò è necessario creare un criterio per tale requisito e passarlo al nostro repository . Quindi, UsersRepository può sedersi su una simulazione anziché su EntityFramework e comunque passare gli stessi criteri attraverso i quali possiamo testare.

Non è perfetto per testare EntityFramework (a causa di cose come DbFunctions che usiamo per Shims), ma copre la maggior parte delle situazioni senza troppa preoccupazione.

    
risposta data 11.09.2015 - 13:35
fonte

Leggi altre domande sui tag