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
Sfruttare GPT-4o realtime su Azure Open AI per conversazioni vocali
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Effettuare il binding di date in Blazor
Aggiungere interattività lato server in Blazor 8
Limitare le richieste lato server con l'interactive routing di Blazor 8
Gestire liste di tipi semplici con Entity Framework Core
Eseguire una query su SQL Azure tramite un workflow di GitHub
Evitare il flickering dei componenti nel prerender di Blazor 8
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Creare una libreria CSS universale: Clip-path
Gestire la cancellazione di una richiesta in streaming da Blazor
I più letti di oggi
- Effettuare il log delle chiamate a function di GPT in ASP.NET Web API
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- Utilizzare il metodo CountBy di LINQ per semplificare raggruppamenti e i conteggi
- Creare una libreria CSS universale: Cards
- Eseguire script pre e post esecuzione di un workflow di GitHub