In progetti di una certa rilevanza, usare un IoC container porta un gran numero di benefici:
- ci consente di astrarre le dipendenze, disaccoppiando quindi l'interfaccia dall'effettiva implementazione;
- facilita di molto la creazione di oggetti con grafi di dipendenze complessi (A che dipende da B, che richiede C e D, ecc..);
- gestisce in maniera del tutto automatica il ciclo di vita delle istanze create.
Esistono diversi IoC container in circolazione, praticamente tutti open source e gratuiti e, tra questi, Ninject è uno dei più versatili e semplici da utilizzare. Proviamo a capire come integrarlo nella nostra solution con un esempio.
Il progetto
Immaginiamo intanto di aver definito una classe Customer nel progetto, e di utilizzarla all'interno di un context Entity Framework:public class Customer { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } } public class MyContext : DbContext { public DbSet<Customer> Customers { get; set; } }
Sebbene possiamo utilizzare direttamente MyContext all'interno dei nostri controller, solitamente si preferisce schermane l'implementazione dietro a un oggetto Repository, la cui interfaccia è grossomodo la seguente:
public interface IRepository<T> { IQueryable<T> Get(); void Add(T entity); void Delete(T entity); void Save(); }
Questo repository, poi, avrà un'implementazione concreta per la classe Customer in un CustomerRepository, che al suo interno sfrutterà il context di Entity Framework:
public class CustomerRepository : IRepository<Customer> { private MyContext _context; public CustomerRepository(MyContext context) { _context = context; } public IQueryable<Customer> Get() { return _context.Customers; } // altri metodi ... }
La dipendenza nei controller di ASP.NET MVC
Le pagine di gestione dei Customer, nella nostra applicazione, saranno realizzate a partire da un CustomerController, che utilizzerà il repository visto in precedenza per accedere allo strato dati.Per disaccoppiare l'implementazione dall'interfaccia, però, non useremo direttamente CustomerRepository, ma ci riferiremo a esso solo tramite la sua interfaccia IRepository
public class CustomersController : Controller { private IRepository<Customer> _customers; public CustomersController(IRepository<Customer> customers) { _customers = customers; } // ... qui vanno le varie action ... }
In questo modo, abbiamo definito due dipendenze:
- CustomerController dipende da IRepository
, che concretamente è implementato da CustomerRepository; - CustomerRepository a sua volta dipende da MyContext.
In particolare, questo controller, così costruito, non è direttamente utilizzabile da ASP.NET MVC 5, perchè non possiede un costruttore senza parametri. Se provassimo a eseguire l'applicazione, a questo punto, riceveremmo un errore simile al seguente:
Enter Ninject e Ninject.MVC3
Un IoC container come Ninject serve proprio a preoccuparsi di risolvere le dipendenze e a istanziare semplicemente i vari oggetti. Vediamo come fare.
Il primo passo consiste nell'includere nella nostra solution, il package NuGet Ninject.MVC3.
Questo package installa automaticamente una serie di librerie (tra cui, per l'appunto, Ninject stesso) e aggiunge alla cartella App_Start del progetto, una classe NinjectWebCommon che viene automaticamente eseguita allo startup dell'applicazione.
NinjectWebCommon contiene il codice necessario a configurare in ASP.NET MVC un custom ControllerFactory, in modo che sia Ninject a preoccuparsi di istanziare i vari controller e non più ASP.NET MVC; oltre questo, è presente anche un entry point, denominato RegisterServices, in cui possiamo configurare le varie dipendenze, come nell'esempio seguente:
private static void RegisterServices(IKernel kernel) { kernel.Bind<IRepository<Customer>>().To<CustomerRepository>(); kernel.Bind<MyContext>().ToSelf().InRequestScope(); }
Nel codice in alto, abbiamo impostato Ninject in modo che, quando è richiesto un IRepository
Si tratta di una funzionalità molto potente, che ci permette di associare la stessa istanza di context anche a più repository, e di avere la certezza che esso sia disponibile fino al termine dell'esecuzione della richiesta.
Questo è tutto ciò che dobbiamo fare per riuscire finalmente a eseguire il nostro CustomerController, il cui costruttore, lo ricordiamo, richiedeva un IRepository
public CustomersController(IRepository<Customer> customers) { _customers = customers; }
Alla richiesta, infatti, Ninject si preoccuperà automaticamente di creare un'istanza di CustomerRepository e di passarlo al costruttore, così che possiamo utilizzarlo all'interno delle varie action.
Conclusioni
Applicazioni complesse spesso definiscono molteplici dipendenze tra i vari oggetti. L'utilizzo di un IoC container come Ninject permette di semplificare di molto il processo di istanziazione e di gestione del ciclo di vita di questi ultimi.Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Ottimizzare le performance delle collection con le classi FrozenSet e FrozenDictionary
Effettuare il deploy di immagini solo da container registry approvati in Kubernetes
Specificare il versioning nel path degli URL in ASP.NET Web API
Disabilitare automaticamente un workflow di GitHub (parte 2)
Sfruttare al massimo i topic space di Event Grid MQTT
Eseguire una GroupBy per entity in Entity Framework
Utilizzare l'operatore GroupBy come ultima istruzione di una query LINQ in Entity Framework
Eseguire operazioni sui blob con Azure Storage Actions
Utilizzare HiLo per ottimizzare le insert in un database con Entity Framework
Le novità di Entity Framework 8
I più letti di oggi
- I nuovi metodi degli array di ECMAScript 5
- Evitare (o ridurre) il repo-jacking sulle GitHub Actions
- Un custom control BoundField con dropdownlist
- .NET Core 3, C#8 and beyond
- Utilizzare long polling in HTML5 per richieste in real time
- Utilizzare le shortcut da tastiera con KeyboardAccelerator nella Universal Windows Platform
- Microsoft Security Bulletin MS05-048