Dependency Injection e Services in ASP.NET Core

Cosa sono e differenze tra: Singleton, Scoped, Transient

Stefano Marchisio
5 min readAug 9, 2020

--

Introduzione

ASP.NET MVC Core è la nuova versione del framework di Microsoft. Visto che l’obbiettivo principale di Microsoft creare un prodotto che non fosse più legato a un server Windows, è stata necessaria una completa riscrittura per eliminare ogni dipendenza dal server sottostante. Infatti il nuovo framework gira anche su Linux e Mec.

Per questo motivo sono state inserite “nativamente” molte cose che nella versione precedente potevano solo essere solo aggiunte tramite librerie esterne.

Tra le molte novità spicca il nuovo motore di “Dependency Injection / Ioc”. Senza entrare troppo nel dettaglio, il concetto base della “Dependency Injection” consiste nell’avere un oggetto separato che si occupa della risoluzione delle dipendenze ovvero della loro creazione ed inizializzazione. Queste dipendenze verranno poi iniettate come parametro nel costruttore di un controller, di una classe di business o altro. In questo modo se si ha l’accortezza di usare le interfacce, si otterrà codice debolmente accoppiato e modulare. Che si presta agli unit test.

Una cosa che valeva anche nella versione precedente era questa. All’interno di un applicazione MVC sono definite varie rotte, quando l’utente digita qualche cosa nel browser il controllo passava al routing, che in base a ciò che è stato digitato stabilisce quale “controller” si deve occupare della richiesta. A questo punto il routing crea in istanza del “controller” e richiama la “action” opportuna. Il nuovo motore di “Dependency Injection” estende la cosa e fa si che dopo che un controller è stato creato vengano anche popolati i parametri del costruttore. Per esempio iniettando automaticamente il DbContext di EntityFramework (se richiesto).

Ciclo di vita e “Dependency Injection”

Ogni oggetto iniettato ha un proprio ciclo di vita che sarà gestito dal motore di dependency injection, infatti sarà lui che provvederà a crearlo e a distruggerlo in base a ciò che abbiamo deciso. In ASP.NET Core possiamo avere 3 tipi di oggetti (con vita diversa): Singleton, Scoped, Transient.

Singleton: Un servizio Singleton viene creato solo una volta e quell’istanza viene condivisa ed utilizzata per tutta la durata dell’applicazione. Un servizio Singleton viene creato quando viene richiesto per la prima volta. Per questo motivo, visto che è un servizio condiviso non deve essere usato per memorizzare uno stato. Come suggerisce il nome il metodo AddSingleton() crea un servizio Singleton.

Scoped: Un servizio Scoped viene creato per ogni richiesta e quell’istanza viene condivisa ed utilizzata per tutta la durata della richiesta. Cioè, un’applicazione crea un istanza a fronte di una richiesta http, e quell’ istanza verrà poi condivisa in tutti i posti in cui sarà richiesta nell’ambito della stessa richiesta. Sarà sempre iniettata la stessa istanza. Come suggerisce il nome il metodo AddScoped() crea un servizio Scoped.

Transient: Un servizio Transient viene creato tutte le volte che è richiesto. Verrà sempre iniettata una nuova istanza, anche se la richiesta http è la stessa. Come suggerisce il nome il metodo AddTransient () crea un servizio Transient.

Ovviamente la dependency injection deve essere opportunamente configurata, altrimenti il motore di dependency injection non sa cosa iniettare!

Startup.cs e ConfigureServices() / Configure()

La classe Startup.cs contiene 2 metodi: ConfigureServices() / Configure() che vengono richiamati allo startup dell’applicazione. Nel metodo ConfigureServices() dichiariamo ciò che dovrà poi essere iniettato (configuriamo la dependency injection). Nel metodo Configure() definiamo la pipeline della nostra applicazione ed i relativi Middleware, questi non saranno trattati nel presente articolo.

Maggiori informazioni su pipeline e Middleware al seguente quì

In ASP.NET Core abbiamo 2 tipi di servizi che possono essere iniettati

· oggetti buildin

· oggetti custom

Sono da considerarsi “servizi buildin” tutti quei servizi legati a qualche funzionalità presente nel framework o aggiunta tramite NuGet: AppSetting, DbContext di EntityFramework, Autenticazione, etc … Questi servizi avranno poi un proprio ciclo di vita e potranno essere: Singleton, Scoped, Transient (anche se questo non è deciso da noi). Nel metodo ConficureServices() è anche possibile effettuare la configurazione di un servizio. Alcuni servizi sono disponibili anche senza essere registrati nel metodo ConficureServices(): IApplicationBuilder, IHostingEnvironment.

Sono da considerarsi “servizi custom” tutti quei servizi definiti in una nostra libreria (per esempio servizi di business). Questi servizi possono essere iniettati come: Singleton, Scoped, Transient (in questo caso siamo noi a decidere la modalità).

Oggetti buildin

Sotto possiamo vedere come definire l’ AppSetting ed il DbContext di EntityFramework, ma le stesse considerazioni valgono per tutto il resto.

AppSettings

public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection(“AppSettings”));
}

Il codice sovrastante configura l’ oggetto AppSettings i cui valori verranno letti dal file appsettings.json. In questo modo sarà possibile iniettarlo all’interno di un controller o in una classe di business tutte le volte che è richiesto. Maggiori informazioni sull’ “options pattern” è possibile trovarle qui.

public class AuthController : Controller
{
private AppSettings appsettings;
private IHostingEnvironment host;
public AuthController(IOptions<AppSettings> options, IHostingEnvironment server)
{
appsettings = options.Value;
host = server;
}
}

Nel codice sovrastante possiamo vedere un controller nel quale è iniettato l’oggetto AppSettings.

DbContext di EntityFramework

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<appstudentiContext>(options => options.UseSqlServer(
Configuration.GetConnectionString(“DefaultConnection”)));
}

Il codice sovrastante configura l’ oggetto DbContext di EntityFramework. In questo modo sarà possibile iniettarlo all’interno di un controller o in una classe di business tutte le volte che è richiesto.

public class CoursesManager : ICoursesManager
{
private AppSettings appsettings;
private appstudentiContext db;
public CoursesManager(IOptions<AppSettings> options, appstudentiContext context)
{
appsettings = options.Value;
db = context;
}
}

Nel codice sovrastante possiamo vedere una classe di business nel quale è iniettato il DbContext di EntityFramework. Quest’oggetto viene iniettato in modalità scoped.

Oggetti custom

Come già accennato possiamo anche creare dei nostri servizi da farci iniettare dove richiesto.

public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ITeachersManager, TeachersManager>();
services.AddTransient<IStudentsManager, StudentsManager>();
}

Il codice sovrastante registra 2 oggetti (TeachersManager e StudentsManager) che potranno poi essere iniettati dove richiesto.

public class TeachersController : Controller
{
private AppSettings appsettings;
private IHostingEnvironment host;
private ITeachersManager tm;
public TeachersController(IOptions<AppSettings> options, IHostingEnvironment server, ITeachersManager teachersmanager)
{
appsettings = options.Value;
host = server;
tm = teachersmanager;
}
}

Nel codice sovrastante possiamo vedere un controller nel quale è iniettato un oggetto di tipo ITeachersManager precedentemente registrato in modalità Transient. Verranno anche iniettati AppSettings e IHostingEnvironment.

Conclusioni

Il motore di “Dependency Injection” presente in ASP.NET Core MVC è un passo avanti rispetto alla versione precedente del framework.

Che cosa è ASP.NET MVC Core e vantaggi apportati dal pattern MVC

Se volete contattarmi il mio profilo Linkedin è il seguente: Stefano Marchisio: Consulente freelance Angular ASP.NET MVC C#

--

--