La inyección de dependencias en C# es un salvavidas a la hora de organizar dependencias, especialmente en aplicaciones ASP.NET Core más complejas. Y si ya está familiarizado con IServiceCollection o simplemente desea ceñirse lo más posible a las ofertas DI ya proporcionadas, Scrutor en C# es una gran mejora.
En este artículo, le proporcionaré una descripción general de alto nivel de la inyección de dependencias y Scrutor en C#. Pero a partir de ahí, pasaremos directamente a 3 consejos que puede utilizar con Scrutor y que podrían resultar muy útiles en su aplicación.
Scrutor es un potente paquete NuGet en C# que mejora la inyección de dependencias. Simplifica el registro y la resolución de dependencias, facilitándole la gestión y organización de su código.
La inyección de dependencia es un enfoque popular para configurar aplicaciones que promueve el acoplamiento flexible y mejora la capacidad de prueba y el mantenimiento. Implica inyectar dependencias en una clase en lugar de crearlas directamente dentro de la clase. Esto significa que la responsabilidad de crear la dependencia no es propiedad de la clase que la requiere, sino de alguien en la pila de llamadas. Al final, esto empuja casi toda la creación de dependencias al punto de entrada de una aplicación, haciéndola difícil de manejar. Sin embargo, los marcos de inyección de dependencias ayudan a limpiar y organizar toda esta lógica .
Scrutor lleva este concepto un paso más allá al proporcionar una forma sencilla e intuitiva de registrar y resolver dependencias. Con Scrutor, ya no es necesario registrar manualmente cada dependencia una por una. En su lugar, puede utilizar convenciones y atributos para automatizar el proceso.
Para ilustrar cómo usar Scrutor en C# para registro y resolución, consideremos un escenario simple. Imaginemos que tenemos una aplicación que requiere el uso de diferentes repositorios de datos, como UserRepository y ProductRepostiory.
Primero, necesitamos instalar el paquete Scrutor NuGet en nuestro proyecto. Abra la Consola del Administrador de paquetes y ejecute el siguiente comando:
Install-Package Scrutor
A continuación, necesitamos definir nuestros repositorios y sus interfaces correspondientes. Este ejemplo está esencialmente vacío, pero nos referiremos a estos en un momento:
public interface IUserRepository { // Interface methods } public class UserRepository : IUserRepository { // Implementation } public interface IProductRepository { // Interface methods } public class ProductRepository : IProductRepository { // Implementation }
Ahora, conectemos nuestras dependencias usando Scrutor. En su código de inicio (como en el método ConfigureServices en ASP.NET Core), agregue el siguiente código:
services.Scan(scan => scan .FromAssemblyOf<Startup>() .AddClasses(classes => classes.AssignableToAny( typeof(IUserRepository), typeof(IProductRepository))) .AsImplementedInterfaces() .WithScopedLifetime());
Este código utiliza el método Scan de Scrutor para escanear el ensamblado que contiene la clase Startup. Luego filtra las clases que se pueden asignar a las interfaces IUserRepository o IProductRepository. Finalmente, mapea las implementaciones de estas interfaces y las registra con sus respectivas interfaces.
Ahora podemos inyectar estas dependencias en nuestras clases. Por ejemplo, digamos que tenemos una clase UserService que requiere IUserRepository:
public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } // Rest of the class implementation }
Al declarar IUserRepository como una dependencia en el constructor, Scrutor junto con IServiceCollection resolverán e inyectarán automáticamente la instancia de UserRepository por nosotros.
Imagine que tiene una interfaz de servicio IService
y una implementación MyService
. Desea registrar la entrada y salida de cada llamada a método en MyService
sin contaminar su lógica empresarial con problemas de registro.
Primero, defina la interfaz IService
y su implementación:
public interface IService { void DoWork(); } public class MyService : IService { public void DoWork() { // Business logic here } }
A continuación, cree una clase decoradora LoggingDecorator
que implemente IService
. Esta clase envolverá el servicio real y agregará registros alrededor de las llamadas a métodos delegados:
public class LoggingDecorator : IService { private readonly IService _decorated; private readonly ILogger<LoggingDecorator> _logger; public LoggingDecorator(IService decorated, ILogger<LoggingDecorator> logger) { _decorated = decorated; _logger = logger; } public void DoWork() { _logger.LogInformation("Starting work."); _decorated.DoWork(); _logger.LogInformation("Finished work."); } }
Ahora, use Scrutor para registrar su servicio y su decorador en Startup.cs
o dondequiera que configure sus servicios:
public void ConfigureServices(IServiceCollection services) { // Register the base service services.AddScoped<IService, MyService>(); // Use Scrutor to apply the decorator services.Decorate<IService, LoggingDecorator>(); // Make sure to register the ILogger or ILogger<T> dependencies if not already done }
Esta configuración utiliza Scrutor para envolver el registro IService
con LoggingDecorator
. Al resolver IService
, el contenedor DI proporcionará una instancia de LoggingDecorator
, que a su vez encapsula una instancia de MyService
. Este enfoque logra la separación de preocupaciones manteniendo la lógica de registro fuera de su lógica empresarial.
Cuando construimos sistemas complejos, a menudo hay casos en los que queremos introducir algo como indicadores de funciones. Estos nos permiten tomar diferentes rutas de código según la configuración y no tener que volver a compilar e implementar una aplicación completa. En algunas situaciones, no se trata sólo de elegir una ruta de código diferente, ¡sino de intercambiar una implementación completa de algo!
¡Probemos un ejemplo juntos! Suponga que tiene una interfaz IFeatureService
con múltiples implementaciones: NewFeatureService
(una característica nueva y experimental) y StandardFeatureService
(la característica estable actual). Desea cambiar entre estas implementaciones en tiempo de ejecución según un indicador de configuración.
Primero, defina la interfaz IFeatureService
y sus implementaciones:
public interface IFeatureService { void ExecuteFeature(); } public class NewFeatureService : IFeatureService { public void ExecuteFeature() { // New feature logic } } public class StandardFeatureService : IFeatureService { public void ExecuteFeature() { // Standard feature logic } }
A continuación, necesita una forma de determinar qué implementación utilizar en función de una configuración de alternancia de funciones. Podría ser un valor en appsettings.json
, una configuración de base de datos o cualquier otra fuente de configuración.
Ahora, use Scrutor para registrar dinámicamente la implementación de servicio adecuada según la función de alternancia:
public void ConfigureServices(IServiceCollection services) { // Assume GetFeatureToggleValue() retrieves the current feature toggle setting var useNewFeature = GetFeatureToggleValue("UseNewFeature"); // Dynamically register the appropriate implementation based on the feature toggle if (useNewFeature) { services.AddScoped<IFeatureService, NewFeatureService>(); } else { services.AddScoped<IFeatureService, StandardFeatureService>(); } // Optional: You could combine this with the previous example // to use the decorator pattern too! // services.Decorate<IFeatureService, FeatureServiceProxy>(); } private bool GetFeatureToggleValue(string featureName) { // This method would typically check your // configuration source to determine if the feature is enabled // For simplicity, this is just a placeholder return false; // or true based on actual configuration }
Adoptar un enfoque como este puede ayudarnos con:
En este artículo, le proporcioné 3 consejos sencillos para la inyección de dependencias con Scrutor en C#. Después de una breve descripción general de qué es la inyección de dependencia y cómo encaja Scrutor en ella, pasamos directamente a nuestros ejemplos. ¡Pudimos ver el escaneo de ensamblajes, cómo decorar dependencias e incluso cómo considerar funciones que marcan implementaciones completas!
La inyección de dependencia es algo que puede simplificar enormemente sus aplicaciones a medida que crecen en complejidad, y Scrutor puede ser de gran ayuda si desea ceñirse a la oferta integrada IServiceCollection de Microsoft . Si esto le resultó útil y está buscando más oportunidades de aprendizaje, considere suscribirse a mi boletín semanal gratuito de ingeniería de software y vea mis videos gratuitos en YouTube . ¡Únase a mí y a los demás miembros de la comunidad en Discord !
También publicado aquí.