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
Gestire liste di tipi semplici con Entity Framework Core
Recuperare un elemento inserito nella cache del browser tramite API JavaScript
Sfruttare lo streaming di una chiamata Http da Blazor
Utilizzare la versione generica di EntityTypeConfiguration in Entity Framework Core
Effettuare il download di un file via FTP con la libreria FluentFTP di .NET
Controllare gli accessi IP alle app con Azure Container Apps
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Miglioramenti nell'accessibilità con Angular CDK
Generare token per autenicarsi sulle API di GitHub
Gestire undefined e partial nelle reactive forms di Angular
Applicare il versioning ai nostri endpoint ASP.NET Core Minimal API
Usare ASP.NET Core dev tunnels per testare le applicazioni su internet
I più letti di oggi
- Autenticarsi in modo sicuro su Azure tramite GitHub Actions
- Miglioramenti nelle performance di Angular 16
- Utilizzare .NET Core con le Azure Function
- Sfruttare al massimo i topic space di Event Grid MQTT
- Recuperare informazioni sul browser con Angular CDK
- Miglioramenti agli screen reader e al contrasto in Angular
- Effettuare il binding di date in Blazor