
Tra le numerose novità introdotte in ASP.NET Core, una delle più importanti é la presenza di un motore di “Dependency Injection” incorporato.
Mentre il template di Visual Studio per un progetto ASP.NET Core MVC include già la configurazione necessaria per utilizzarla, quando creiamo una console application .NET Core non abbiamo nessun aiuto.
Vediamo quindi come possiamo risolvere questo problema. Anzitutto creiamo nel nostro progetto due interfacce.
In questa demo ho utilizzato la versione 2.2 di .NET Core.
namespace DemoArticolo.Interfaces
{
public interface InterfaceA
{
void DoSomething();
}
public interface InterfaceB
{
void DoSomethingElse();
}
}
Creiamo quindi due classi che implementino queste interfacce.
public class ClassA : InterfaceA
{
public void DoSomething()
{
Console.WriteLine("Writing something to the console from ClassA");
}
}
Nel costruttore della classe ClassB è stata iniettata l’interfaccia interfaceA.
Occorre a questo punto installare tramite NuGet il seguente pacchetto: Microsoft.Extensions.DependencyInjection

Abbiamo quindi bisogno di un metodo che aggiunga ed eventualmente configuri i nostri servizi.
private static void ConfigureServices(ServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<InterfaceA, ClassA>();
serviceCollection.AddSingleton<InterfaceB, ClassB>();
}
Abbiamo scelto il metodo AddSingleton() che utilizzerà sempre la stessa istanza di ClassA e ClassB per ogni richiesta.
È possibile, comunque, fare scelte diverse, quali ad esempio AddScoped() o AddTransient() in base al contesto in cui state inserendo il codice. La documentazione completa è disponibile al seguente indirizzo
Il metodo ConfigureServices() è invocato nel metodo Main() della nostra console application, per creare la ServiceCollection che utilizzeremo nel corso dell’applicazione.
Va quindi costruito un ServiceProvider, ossia l’oggetto che è in grado di recuperare le istanze dei nostri servizi.
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
}
A questo punto, dobbiamo solo utilizzare le istanze dei nostri servizi:
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
var implementazione = serviceProvider.GetService<InterfaceB>();
implementazione.DoSomethingElse();
}

Un esempio più concreto di questa tecnica è quello del Logging in .NET Core, una API che è compatibile con un gran numero di provider di logging di terze parti.
È necessario installare due altri pacchetti da NuGet: Microsoft.Extensions.Logging e Microsoft.Extensions.Logging.Console

Modifichiamo il metodo ConfigureService, per registrare il servizio di Logging tramite console.
private static void ConfigureServices(ServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<InterfaceA, ClassA>();
serviceCollection.AddSingleton<InterfaceB, ClassB>();
serviceCollection.AddLogging(configure => configure.AddConsole());
}
Modifichiamo la ClassB iniettando, tramite il costruttore, il servizio di Logging.
public class ClassB : InterfaceB
{
private readonly InterfaceA interfaceA;
private readonly ILogger<Program> logger;
public ClassB(InterfaceA _interfaceA, ILogger<Program> _logger)
{
interfaceA = _interfaceA;
logger = _logger;
}
public void DoSomethingElse()
{
interfaceA.DoSomething();
logger.LogInformation("Logging from InterfaceB");
}
}
Modifichiamo, infine, il method Main() per visualizzare l’effetto del logging nel terminale.
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();
logger.LogWarning("Hello, Dependency");
var implementazione = serviceProvider.GetService<InterfaceB>();
implementazione.DoSomethingElse();
Console.ReadLine();
}

Come ultimo esempio, utilizziamo Serilog, una libreria di logging molto diffusa che è compatibile sia con .NET, che con .NET Core. I pacchetti da installare sono i seguenti:

Modifichiamo il metodo ConfigureServices, in maniera tale da registrare Serilog. Scegliamo inizialmente di eseguire il log solo su console.
private static void ConfigureServices(ServiceCollection serviceCollection)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
serviceCollection.AddSingleton<InterfaceA, ClassA>();
serviceCollection.AddSingleton<InterfaceB, ClassB>();
// serviceCollection.AddLogging(configure => configure.AddConsole());
serviceCollection.AddLogging(configure => configure.AddSerilog());
}
L’output viene modificato (e ricordiamo che può essere ulteriormente configurato).

Possiamo visualizzare i messaggi di log in console e contemporaneamente salvarli su file. La modifica di ConfigureServices() è molto semplice.
private static void ConfigureServices(ServiceCollection serviceCollection)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
serviceCollection.AddSingleton<InterfaceA, ClassA>();
serviceCollection.AddSingleton<InterfaceB, ClassB>();
// serviceCollection.AddLogging(configure => configure.AddConsole());
serviceCollection.AddLogging(configure => configure.AddSerilog());
}

Esistono numerosi altri pacchetti che gestiscono il logging di Serilog. Tali pacchetti vengono definiti Sink. A questo indirizzo troverete l’elenco completo di sink disponibili per Serilog.
Il codice citato in questo articolo è disponibile su GitHub al seguente link