Quando utilizzare il modello di repository


20

Ho letto di recente che non è buona norma utilizzare il modello di repository insieme a un ORM. Da quanto ho capito, questo è perché l'astrazione che forniscono sul database SQL è troppo permissiva per essere contenuta dal modello.

Ho un paio di domande su questo:

  1. Cosa fai se vuoi cambiare ORM? Si avrebbe un codice specifico ORM nella propria applicazione se non lo si contiene in un repository.

  2. Il modello di repository è ancora valido quando non si utilizza un ORM e si utilizza ADO.NET per l'accesso ai dati e il popolamento dei dati degli oggetti da soli?

  3. Se si utilizza un ORM ma non il modello di repository, dove vengono conservate le query più utilizzate? Sarebbe saggio rappresentare ogni query come una classe e avere una sorta di factory di query per creare istanze?


1
1) non sarai mai in grado di scambiare un ORM a causa dei loro diversi comportamenti, anche se entrambi supportano linq, si comporteranno in modo abbastanza diverso da interrompere l'applicazione. ad es. comportamento di caricamento lento, tracciamento sporco, proxy fantasma ecc. È comunque bello poter sostituire l'ORM con un'implementazione in-mem per i test ..
Roger Johansson,

Per quello che vale, la mia opinione sulla discussione su repository / ORM è qui: stackoverflow.com/questions/13180501/…
Eric King,

Dove hai letto che è una cattiva pratica?
Dave Hillier,

Risposte:


3

1) Vero, ma con quale frequenza si cambia un ORM?
2) Direi di sì, perché un contesto ORM è una specie di repository e nasconde molto lavoro che comporta la creazione di query, il recupero di dati, il mapping, ecc. Se non si utilizza un ORM, tale logica deve risiedere da qualche parte. Non so se ciò si qualificherebbe come modello di repository nel senso più stretto della parola però ...
3) Incapsulare query è qualcosa che vedo frequentemente, ma di solito è più per scopi di test / stub. A parte questo, starei attento quando riutilizzi una query in diverse parti di un'applicazione, perché rischi di creare una dipendenza da qualcosa che potrebbe cambiare n-volte (n è il numero di posti in cui usi la query).


1
1) Stai facendo la domanda sbagliata. Non importa quanto spesso, deve essere solo una volta. Dato il numero di opzioni ORM disponibili, potrebbe essercene una migliore di quella che stai già utilizzando. La domanda ora diventa: cosa succede quando lo fai? Il repository ti dà una bella astrazione. Sono stato lì e vorrei aver fatto una tale astrazione in primo luogo.
Devnull,

3
@devnull Non sono d'accordo. Se accadesse al massimo una volta, lo considererei un rischio accettabile. Se temi di fare la scelta sbagliata: sperimenta di più prima di impegnarti. In teoria, una simile astrazione suona bene, ma in pratica finisci per ricreare una parte abbastanza grande dell'api del tuo ORM, tutto perché potresti finire per sceglierne un altro, un giorno o l'altro. Per me, questo è uno sforzo sprecato, codice ridondante e più codice che tu stesso devi mantenere e garantire. Inoltre, la commutazione di un ORM non dovrebbe influire su un'intera applicazione; imparare a posizionare i confini del dominio.
Stefan Billiet,

La modifica ORM non è l'unica ragione per il repository. Se è necessario introdurre la cache [distribuita] nella propria applicazione, tutte le modifiche verranno eseguite nel repository e il BL non cambierà a causa delle modifiche del livello di accesso ai dati.
Valery,

la cache distribuita non sarebbe integrata nell'ORM nella maggior parte dei casi?
user1450877

Penso che dipenda dall'ORM. Puoi decidere di utilizzare ORM più leggero di NHibernate o EF.
Valery,

2

1) Cosa fare se si desidera cambiare ORM, si avrebbe un codice ORM specifico nella propria applicazione se non lo si contiene in un repository.

Non sono ancora stato in una posizione in cui all'improvviso un'azienda ha deciso di cambiare tecnologia di accesso ai dati. Se ciò accade, sarà necessario un po 'di lavoro. Tendo ad astrarre le operazioni di accesso ai dati attraverso le interfacce. Il repository è un modo per risolverlo.

Avrei quindi un assemblaggio diverso per l'implementazione concreta del mio livello di accesso ai dati. Ad esempio, potrei avere:

Company.Product.Datae Company.Product.Data.EntityFrameworkassemblee. Il primo assembly verrebbe utilizzato esclusivamente per le interfacce, mentre un altro sarebbe un'implementazione concreta della logica di accesso ai dati di Entity Framework.

2) Il modello di repository è ancora valido quando non si utilizza un ORM e si utilizza ADO.net per l'accesso ai dati e il popolamento dei dati degli oggetti da soli?

Penso che spetti a te decidere quale modello è valido o meno. Ho usato un modello di repository nel livello di presentazione. Una cosa da tenere a mente è che alla gente piace gettare le responsabilità nei repository. Prima che tu lo sappia, la tua classe di repository ballerà, canterà e farà ogni sorta di cose. Vuoi evitare questo.

Ho visto una classe di repository che è iniziata con responsabilità GetAll, GetById, Update ed Delete, il che va bene. Quando il progetto fu completato, quella stessa classe aveva dozzine di metodi (responsabilità) che non avrebbero mai dovuto essere lì. Ad esempio GetByForename, GetBySurname, UpdateWithExclusions e tutti i tipi di cose folli.

Qui entrano in gioco query e comandi.

3) Se si utilizza un ORM ma non il modello di repository, dove vengono mantenute le query di uso comune. Sarebbe saggio rappresentare ogni query come una classe e avere una sorta di factory di query per creare istanze?

Penso che sia una buona idea usare query e comandi invece di repository. Faccio quanto segue:

  • Definire l'interfaccia per una query. Questo ti aiuterà a testare l'unità. Per esempiopublic interface IGetProductsByCategoryQuery { ... }

  • Definire un'implementazione concreta per una query. Sarai in grado di iniettarli attraverso il framework IoC di tua scelta. Per esempiopublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Ora invece di inquinare il repository con dozzine di responsabilità, raggruppo semplicemente le mie query in spazi dei nomi. Ad esempio, un'interfaccia per la query sopra può risiedere in: Company.SolutionName.Products.Queriese l'implementazione può risiedere inCompany.SolutionName.Products.Queries.Implementation

Quando si tratta di aggiornare o rimuovere i dati, utilizzo il modello di comando allo stesso modo.

Alcuni potrebbero non essere d'accordo e dire che prima che il progetto sia completo avrai decine di classi e spazi dei nomi. Sì lo farai. Nella mia mente è una buona cosa come puoi navigare attraverso la soluzione in IDE di tua scelta e vedere immediatamente quale tipo di responsabilità ha un determinato componente. Se invece hai deciso di utilizzare un modello di repository, dovrai guardare all'interno di ogni classe di repository cercando di capire le sue responsabilità.


Mi piace l'idea di avere comandi invece di funzioni generiche. Dove posso leggere di più su come implementarli nel contesto dell'accesso ai dati?
ankush981

1

DICHIARAZIONE DI NON RESPONSABILITÀ: Ciò che segue si basa sulla mia comprensione e breve esperienza di detto modello (ovvero Repository). Potrei sbagliarmi ... in effetti, sono abbastanza sicuro di sbagliarmi :). Quindi, mentre questo è un tentativo di risposta, è anche una domanda sotto mentite spoglie.

Sto usando il modello di repository per sottrarre il livello di accesso ai dati, che nella maggior parte dei casi è un ORM. Si tratta di un'interfaccia generica, con metodi per creare, leggere, aggiornare, eliminare e classi di implementazione per LINQ to SQL ed EF finora. Potrei realizzare un'implementazione che scrive su file XML su disco (solo un esempio selvaggio di cosa potrei farci). Non sto implementando Unit of Work poiché è supportato dagli ORM. Se necessario, probabilmente potrei implementarlo. Mi piace così perché, finora, mi ha dato una bella separazione. Non sto dicendo che non ci siano alternative migliori.

Per rispondere alle tue domande:

  1. Che ti piaccia o no, si verificherà il cambiamento. E se hai scritto un sacco di applicazioni e è giunto il momento di mantenerle, ma senti che è un dolore lavorare con l'attuale ORM e vuoi cambiarlo, ti loderai per aver fatto una tale astrazione. Usa semplicemente tutto ciò con cui ti senti a tuo agio, sia esso Repository o altro modello / concetto.
  2. Sì, purché lo usi per separare l'accesso ai dati. Come ho accennato in precedenza, è possibile eseguire un'implementazione che scrive i dati in file flat.
  3. Uso il repository per conservare le mie query e gli oggetti query comunemente usati per quando ho query che voglio modificare (che non sono così tante).

Per fare un altro esempio, i ragazzi di Umbraco hanno estratto il contenitore DI, nel caso in cui volessero passare a qualcos'altro.


0

Se l'ORM offre flessibilità di LINQ, caricamento desideroso ecc. Non lo nasconderei dietro alcun livello aggiuntivo.
Se si tratta di sql grezzo (micro ORM), è necessario utilizzare comunque "metodo per query" per ottenere la riusabilità, facendo in modo che il modello di repository si adatti.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Perché dovresti cambiare?
Dovresti usare quello che ha la maggior parte delle funzionalità che ti servono. È possibile che una nuova versione di ormX apporti nuove funzionalità e risulti migliore di quella attuale, ma ...
Se scegli di nascondere l'orm, puoi utilizzare solo le funzionalità che tutti i candidati hanno in comune.
Ad esempio, non puoi usare le Dictionary<string, Entity>proprietà nelle tue entità perché ormY non può gestirle.

Supponendo che LINQ sia utilizzato, la maggior parte degli switch orm sta semplicemente cambiando i riferimenti della libreria e sostituendoli session.Query<Foo>()con context.Fooso simili fino a quando non viene compilato. Compito noioso, ma richiede meno tempo rispetto alla codifica del livello di astrazione.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Sì. Il codice dovrebbe essere riutilizzabile e ciò significa mettere l'edificio sql, la materializzazione degli oggetti ecc. In un unico posto (classe separata). Puoi anche chiamare la classe "XRepository" ed estrarne un'interfaccia.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Supponendo che LINQ sia utilizzato, un wrapper di classe sarebbe eccessivo per IMHO. Un modo migliore sarebbe metodi di estensione

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

Qualsiasi codice che viene utilizzato in più posizioni (o ha il potenziale) ed è abbastanza complicato (soggetto a errori), deve essere estratto in una posizione centralizzata.

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.