Seleziona una pagina

“I pattern descrivono un problema che si verifica continuamente nel nostro ambiente e descrivono il cuore della soluzione a quel problema in un modo che la soluzione può essere usata altrettanto continuamente, senza doverla fare nello stesso modo due volte”

Un tipico problema che ci troviamo ad affrontare quotidianamente nella nostra vita di sviluppatore è l’accesso ai dati, che possono essere memorizzati su un DataBase o su un File, possono essere distribuiti su più Banche Dati o addirittura su più tecnologie differente. Un altro tipico problema è il forte accoppiamento delle logiche di business con la tecnologia necessaria alla gestione dei dati persistenti ed un cambiamento di versione del motore del database o addirittura l’adozione di un differente RDBMS potrebbe portare con se un corposo Refactoring del sistema, a volte talmente importante che da far abbandonare il proposito.  

Il Pattern Repository risponde al problema di forte accoppiamento tra le logiche di business e l’accesso ai dati introducendo un ulteriore livello, per l’appunto il Repository, il quale sarà il solo “responsabile” del recupero e salvataggio dai dati.

Un tipico schema del Pattern Repository è rappresentato dal seguente schema.

Fig.1

Repository

Il Repository è “responsabile” dell’accesso ai dati, questo cosa significa? Significa che lo strato sovrastante il repository, nello schema proposto il Business Layer, non ha nessuna informazione su dove e come sono memorizzati i dati, o con quale framework e/o linguaggio effettuiamo l’accesso ai dati. Significa in oltre che il repository si fa carico di una serie di compiti quali:

  1. Mapping del modello di business verso il modello dati: il business layer lavora con un proprio Dominio, tipicamente definito Business Model, che non necessariamente coincide con il modello dati.
    Esempio
    public class Post
        {
            [Key]
            public int Id { get; set; }
            public string Titolo { get; set; }
            public string Contenuto { get; set; }
            public List<String> Tags { get; set; }
        }
          

    La classe Post ha 3 proprietà Id ( che è anche la chiave ), Titolo e Contenuto ed ha un’altra proprietà Tags rappresentata da una lista di string, questa classe potrebbe essere rappresentata in diversi modi su un Database:

    Un unica Tabella con i tags memorizzati in un campo varchar in formato json

    tabella1

         
    Oppure con due Tabelle dove i post sono chiave esterna di della tabella Tags

    tabella2

    O con tre Tabelle con con le due tabelle Post e Tags in relazione Molti-Molti

    tabella3

    Il repository dovrà avere conoscenza di come sono realmente strutturati i dati  ed assolvere al compito di mappare la classe Post con la/e tabella/e di destinazione.

  2. Garantire la persistenza dei dati: va da se che se il Repository è l’unico responsabile dell’accesso ai dati esso debba necessariamente salvarli da qualche parte e all’occorrenza recuperarli per renderli nuovamente disponibili.

  3. Espone almeno i metodi CRUD per ogni entità del modello: tipicamente il Repository implementa un interfaccia base come la seguente:

    public interface IRepository<TEntity>
        {
            void  Create(TEntity Obj);
            Task CreateAsync(TEntity Obj);
            IQueryable<TEntity> Get();
            ICollection<TEntity> Where(Expression<Func<TEntity,bool >> predicate);
            TEntity GetById(long id);
            void Update(TEntity Obj);
            Task UpdateAsync(TEntity Obj);
            void Delete(TEntity Obj);
            Task DeleteAsync(TEntity Obj);
    
    
        }
            

    L’interfaccia IRepository<TEntity> espone i metodi minimi CRUD ( Create, Read, Update, Delete ) in particolare per i metodi di scrittura ( Create, Update e Delete ) ne espone anche i metodi asincroni oltre ai metodi sincroni, mentre per il recupero dei dati mette a disposizione i metodi:

    Get() che restituisce un IQueryable<TEntity>, questa interfaccia (IQueryble) è utilizzare per dare un maggiore flessibilità nel recupero dati, infatti la query di estrazione dati, al ritorno del metodo, non è stata ancora eseguita, essa verrà realmente eseguita solo quando il risultato della stessa verrà utilizzato.

    Where ( predicate) che permette di effettuare una ricerca all’interno della tabella introducendo dei filtro con il parametro predicate di tipi Expression<Func<TEntity,bool>>.


Vorrei ancora un attimo soffermarmi sui due metodi di lettura Get e Where proposti nell’interfaccia, a prima vista possono sembrare speculari tra loro, con l’unica differenza che il predicate nel caso del Where è utilizzato come parametro mentre per il Get può essere utilizzato dopo all’interno del metodo Where dell’interfaccia IQueryble.

 void RecuperoDati()
        {
            IRepository<Post> _repository;

            var DatiFromWhere = _repository.Where(o => o.Id == 1).FirstOrDefault();
            var DatiFromGet = _repository.Get().Where(o => o.Id == 1).FirstOrDefault();
        }
  

Nell’esempio sopra entrambi i metodi effettuano la stessa Query, ma nel caso avessimo bisogno di recuperare solo un porzione delle informazioni del modello la situazione cambia.

void RecuperoDatiParziale()
        {
            IRepository<Post> _repository;

            var DatiFromWhere = _repository.Where(o => o.Id == 1).Select ( s => s.Titolo ).FirstOrDefault();
            var DatiFromGet = _repository.Get().Where(o => o.Id == 1).Select (s => s.Titolo ).FirstOrDefault();
        }
  

In questo secondo esempio le query che vengono realmente eseguite sono differenti,la variabile DatiFromWhere viene popolata eseguendo la query su tutta la tabella considerando il where portando il risultato in memoria all’interno di una collection di Post e da questa ne estrae la proprietà Titolo, al contrario la variabile DatiFromGet viene popolata con una query considerando il where ed il select del solo Titolo.Per semplificare la trattazione sull’interfaccia IQueryble ho assunto che i dati fossero memorizzati su un Database ma il discorso è totalmente speculare per altri tipi di supporto e/o tecnologia.

Passiamo adesso ad analizzare i vantaggi offerti dal Repository:

  1. Il Prmo dei vantaggi del pattern Repository, che è anche uno dei motivi che ne ha generato la sua definizione, è la possibilità di poter avere diverse implementazioni da utilizzare a seconda del caso d’uso.

    Ad esempio per garantire l’isolamento negli unit test dalla business logico è opportuno avere a disposizione una implementazione del repository In Memory in cui precaricare i dati necessari all’esecuzione dei test.
  2. Evita codice duplicato, ogni Entità del modello avrà il proprio repository, unico ed universalmente riconosciuto all’interno della soluzione.

    Quante volte vi sarà capitato di scrivere all’interno di due metodi diversi le stesse righe di codice per accedere allo stesso dato?
  3. Alto tasso manutenibilità, le fisiologiche operazioni di refactoring del codice sono molto più semplici da portare avanti.
  4. Alto tasso di testabilità, è possibile ed auspicabile creare un piano di test per ogni implementazione del repository così da diminuire sensibilmente i bug fix.

Nei prossimi articoli tratterò due possibili implementazioni del Repository: DataBase con l’ausilio del sempre verde Entity Framework e In Memory.

Altri Articoli

Principi SOLID – SRP Single Responsability ...
views 240
Con questo articolo cominceremo a rivedere alcuni dei design pattern più importanti e dei principi di di programmazione più usati nello sviluppo di ap...
.Net Standard 2.0 il futuro delle class library
views 118
Cloud First, Mobile First. Questo è il mantra che Satya Nadella ha inculcato nel cambio di pelle e di passo di Microsoft, questo mantra per noi develo...
SmartValidator un idea di fusione tra Generic e De...
views 57
In questo primo post vorrei parlarvi di un mondo fantastico i Generic ed i Delegate, l’intento non è assolutamente quello di essere completame...
Office 2016
views 61
Prima di tutto ringrazio lo staff di DotNetCode per avermi creato questo blog. Questo è il mio primo post su questo blog e prima di passare all'argome...