Gestire l'accesso ai dati direttamente all'interno della business logic non è una buona idea, poiché la diretta implementazione dei vari metodi CRUD va inevitabilmente a creare un forte accoppiamento tra due funzionalità che per loro natura sono differenti. Un approccio del genere causa una serie di svantaggi tra cui:
1. Difficile testabilità della business logic
2. Forte dipendenza da parte della business logic di compoenenti esterne come il database
3. Duplicazione del codice di accesso ai dati all'interno della codebase
4. Difficile implementazione di meccanismi multi-tier dedicati alla cache
In questo articolo vedremo come sia possibile implementare il Repository Pattern sfruttando i tipi generici in modo da risolvere i problemi sopra indicati. Questo approccio può essere utilizzato in tutti quei progetti dove è richiesto un layer, o comunque una serie di funzionalità, di accesso ai dati.
Creazione di un contratto comune per l'entità generica
Come prima cosa è necessario definire un contratto comune che successivamente tutti i nostri tipi concreti andranno a utilizzare, pertanto creiamo una nuova class EntityBase come segue.
namespace ASPItalia.RepositoryPattern { public class EntityBase { public int Id { get; set; } } }
Qualora utilizzassimo EF Code First, la proprietà Id rappresenterà il campo chiave della nostra identità.
Definizione del repository
Andiamo ora a definire tramite un'interfaccia, i vari metodi che il nostro oggetto repository dovrà implementare. Creiamo quindi un'interfaccia chiamata IRepository come segue:
namespace ASPItalia.RepositoryPattern { public interface IRepository<T> where T : EntityBase { IEnumerable<T> List { get; } Task Add(T entity); Task Delete(T entity); Task Update(T entity); Task<T> FindById(int Id); } }
Come possiamo vedere, abbiamo predisposto i vari metodi CRUD utilizzando il tipo di ritorno Task o Task<T> in modo da permettere un facile utilizzo delle versioni asincrone dei metodi di interrogazione esposti da EF.
Creazione di un modello
Creiamo ora una nuova classe, che idealmente rappresenterà un'entità all'interno del nostro database, in modo da renderla "consumabile" tramite la classe di repository che creeremo a breve. Creiamo una classe Customer come segue:
namespace ASPItalia.RepositoryPattern { public partial class Customer : EntityBase { [Required] public String Name { get; set; } } }
Creazione del repository per la classe Customer
Ora che abbiamo definito dei contratti comuni e un modello che rappresenta i nostri dati, andiamo a definire la classe che si occuperà di interrogare la nostra base di dati, restituendo i dati che la nostra business logic tratterà in qualche modo. Creiamo una classe CustomerRepository come segue.
namespace ASPItalia.RepositoryPattern { public class CustomerRepository : IRepository<Customer>, IDisposable { private ApplicationContext dbContext; public CustomerRepository() { dbContext = new ApplicationContext(); } public IEnumerable<Customer> List { get { return dbContext.Customers.ToList(); } } public async Task Add(Customer entity) { if(entity != null) { dbContext.Customers.Add(entity); await dbContext.SaveChangesAsync(); } } public async Task Delete(Customer entity) { if (entity != null) { dbContext.Customers.Remove(entity); await dbContext.SaveChangesAsync(); } } public async Task<Customer> FindById(int Id) { return await dbContext.Customers.FindAsync(Id); } public async Task Update(Customer entity) { dbContext.Entry(entity).State = EntityState.Modified; await dbContext.SaveChangesAsync(); } public void Dispose() { if(dbContext != null) { dbContext.Dispose(); } } } }
In aggiunta all'interfaccia IRepository<T> abbiamo anche implementato l'interfaccia IDisposable per migliorare la gestione della memoria.
Data la semplicità di questo approccio e la completa indipendenza della classe CustomerRepository dalla business logic, la successiva implementazione di una cache in-memory adesso risulta immediata.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Managed deployment strategy in Azure DevOps
Ottimizzare le performance delle collection con le classi FrozenSet e FrozenDictionary
Creare una libreria CSS universale - Rotazione degli elementi
Testare l'invio dei messaggi con Event Hubs Data Explorer
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
Utilizzare Container Queries nominali
Filtering sulle colonne in una QuickGrid di Blazor
Ricevere notifiche sui test con Azure Load Testing
Usare le navigation property in QuickGrid di Blazor
Recuperare App Service cancellati su Azure
Utilizzare il metodo CountBy di LINQ per semplificare raggruppamenti e i conteggi
Selettore CSS :has() e i suoi casi d'uso avanzati