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
Utilizzare Container Queries nominali
Managed deployment strategy in Azure DevOps
Usare il colore CSS per migliorare lo stile della pagina
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Migliorare la scalabilità delle Azure Function con il Flex Consumption
Disabilitare automaticamente un workflow di GitHub
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Configurare lo startup di applicazioni server e client con .NET Aspire
Creare una custom property in GitHub
Eseguire query manipolando le liste contenute in un oggetto mappato verso una colonna JSON
Utilizzare il metodo Index di LINQ per scorrere una lista sapendo anche l'indice dell'elemento
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT