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
Eseguire query manipolando liste di tipi semplici con Entity Framework Core
Filtrare i dati di una QuickGrid in Blazor con una drop down list
Effettuare il refresh dei dati di una QuickGrid di Blazor
Come migrare da una form non tipizzata a una form tipizzata in Angular
Utilizzare Copilot con Azure Cosmos DB
Inference di dati strutturati da testo con Semantic Kernel e ASP.NET Core Web API
Sfruttare al massimo i topic space di Event Grid MQTT
Criptare la comunicazione con mTLS in Azure Container Apps
Triggerare una pipeline su un altro repository di Azure DevOps
Eseguire query per recuperare il padre di un record che sfrutta il tipo HierarchyID in Entity Framework
Aggiungere interattività lato server in Blazor 8
Utilizzare Tailwind CSS all'interno di React: installazione