Scrutor è una libreria .net core disponibile su nuget che consnete di aggiungere una serie di extensions al motore di dependency injection di .net core. Queste estensioni consentono di semplificare e talvolta automatizzare la registrazione di dipendenze a partire dall’assembly in cui sono definite le classi da registrare. Può essere scaricata dal link seguente.
Scrutor consente di ricercare dinamicamente i tipi all’interno di assembly registrandoli a runtime. Sfruttando la scansione può essere automatizzato (almento parzialmente) il codice di registrazione delle dipendenze. Questo tipo di comportamento consente di creare sistemi estendibili e facilmente configurabili.
Supponiamo di avere una progetto webapi in .net core. All’interno del progetto abbiamo una classe City:
public class City
{
public required int Id { get; init; }
public required string Description { get; init; }
public required string State { get; init; }
}Nella nostra applicazione è dichiarata anche un’interfaccia per ottenere la città a partire dal suo id:
namespace Services.Implementations;
public interface ICityService
{
City GetCity(int id);
}Tutto questo si traduce nella creazione di un service che implementa l’interfaccia:
namespace Services.Implementations;
public class CityService : ICityService
{
public City GetCity(int id)
{
return new City
{
Id = id,
Description = "Tortona",
State = "Italy"
};
}
}Esponiamo, quindi, l’api per ottenere la città
[Route("[controller]")]
[ApiController]
public class CitiesController : ControllerBase
{
private readonly IUserService _cityService;
public CitiesController(IUserService cityService)
{
_cityService = cityService;
}
[HttpGet("{id:int}")]
public IActionResult GetCity(int id)
{
return Ok(_cityService.GetCity(id));
}
}A questo punto nel nostro Program.cs è necessario indicare la dependency injection che consente di ottenere l’oggetto a partire dall’interfaccia. Da notare che abbiamo inserito il nostro service all’interno del namerspace Services.Implementations.
Invece di utilizzare la registrazione in maniera tradizionale, possiamo utilizzare l’extension di Scrutor denominata Scan():
builder.Services.Scan(selector => selector
.FromCallingAssembly()
.AddClasses(
classSelector =>
classSelector.InNamespaces("Services.Implementations")
)
.AsImplementedInterfaces()
);il tutto sempre all’interno di Program.cs.
Utilizzando Scan(…) viene eseguita la scansione all’interno dell’assembly estraendo i servizi che dovranno essere utilizzati in fase di registrazione.
Per prima cosa dobbiamo indicare l’assembly all’interno del quale andare a recuperare i tipi target, nell’esempio utilizzando FromCallingAssembly(). Successivamente viene ristretta la ricerca utilizzando tutte le classi che sono presenti all’interno del namespace Services.Implementations.
Al termine richiamiamo il metodo AsImplementedInterfaces() che consente di effettuare la registrazione dei tipi selezionati in sostituzione di tutte le interfacce che le implementano.
Sfruttando questo meccanismo è possibile creare sistemi estensibili e programmi in grado di caricare moduli aggiuntivi in fase di esecuzione.
In questo esempio abbiamo visto come sia possibile registrare classi presenti all’interno dello stesso assembly. Nel prossimo post analizzeremo come sia possibile registrare classi provenienti da assembly differenti.