L’injection de dépendances en C# est une bouée de sauvetage lors de l’organisation des dépendances, en particulier dans les applications ASP.NET Core plus complexes. Et si vous êtes déjà familier avec IServiceCollection ou si vous souhaitez simplement vous en tenir le plus possible aux offres DI déjà fournies, Scrutor en C# est une grande amélioration.
Dans cet article, je vais vous fournir un aperçu de haut niveau de l'injection de dépendances et de Scrutor en C#. Mais à partir de là, nous passons directement à 3 conseils que vous pouvez utiliser avec Scrutor et qui pourraient s'avérer très utiles dans votre candidature !
Scrutor est un puissant package NuGet en C# qui améliore l'injection de dépendances. Il simplifie l'enregistrement et la résolution des dépendances, ce qui vous permet de gérer et d'organiser plus facilement votre code.
L'injection de dépendances est une approche populaire pour configurer des applications qui favorise le couplage lâche et améliore la testabilité et la maintenabilité. Cela implique d’injecter des dépendances dans une classe plutôt que de les créer directement au sein de la classe. Cela signifie que la responsabilité de créer la dépendance n'appartient pas à la classe qui la nécessite, mais plutôt à quelqu'un au niveau de la pile d'appels. Finalement, cela pousse presque toutes les créations de dépendances au point d'entrée d'une application, ce qui la rend lourde. Cependant, les frameworks d'injection de dépendances aident à nettoyer et à organiser toute cette logique .
Scrutor va encore plus loin dans ce concept en fournissant un moyen simple et intuitif d'enregistrer et de résoudre les dépendances. Avec Scrutor, vous n'avez plus besoin d'enregistrer manuellement chaque dépendance une par une. Au lieu de cela, vous pouvez utiliser des conventions et des attributs pour automatiser le processus.
Pour illustrer comment utiliser Scrutor en C# pour l'enregistrement et la résolution, considérons un scénario simple. Imaginez que nous ayons une application qui nécessite l'utilisation de différents référentiels de données, tels que UserRepository et ProductRepostiory.
Tout d’abord, nous devons installer le package Scrutor NuGet dans notre projet. Ouvrez la console du gestionnaire de packages et exécutez la commande suivante :
Install-Package Scrutor
Ensuite, nous devons définir nos référentiels et leurs interfaces correspondantes. Cet exemple est essentiellement vide, mais nous y ferons référence dans un instant :
public interface IUserRepository { // Interface methods } public class UserRepository : IUserRepository { // Implementation } public interface IProductRepository { // Interface methods } public class ProductRepository : IProductRepository { // Implementation }
Maintenant, connectons nos dépendances à l'aide de Scrutor. Dans votre code de démarrage (comme dans la méthode ConfigureServices dans ASP.NET Core), ajoutez le code suivant :
services.Scan(scan => scan .FromAssemblyOf<Startup>() .AddClasses(classes => classes.AssignableToAny( typeof(IUserRepository), typeof(IProductRepository))) .AsImplementedInterfaces() .WithScopedLifetime());
Ce code utilise la méthode Scan de Scrutor pour analyser l'assembly qui contient la classe Startup. Il filtre ensuite les classes attribuables aux interfaces IUserRepository ou IProductRepository. Enfin, il cartographie les implémentations de ces interfaces et les enregistre auprès de leurs interfaces respectives.
Maintenant, nous pouvons injecter ces dépendances dans nos classes. Par exemple, disons que nous avons une classe UserService qui nécessite IUserRepository :
public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } // Rest of the class implementation }
En déclarant IUserRepository comme dépendance dans le constructeur, Scrutor avec IServiceCollection résoudra et injectera automatiquement l'instance UserRepository pour nous.
Imaginez que vous ayez une interface de service IService
et une implémentation MyService
. Vous souhaitez enregistrer l'entrée et la sortie de chaque appel de méthode dans MyService
sans polluer sa logique métier avec des problèmes de journalisation.
Tout d’abord, définissez l’interface IService
et son implémentation :
public interface IService { void DoWork(); } public class MyService : IService { public void DoWork() { // Business logic here } }
Ensuite, créez une classe de décorateur LoggingDecorator
qui implémente IService
. Cette classe encapsulera le service réel et ajoutera une journalisation autour des appels de méthode délégués :
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."); } }
Maintenant, utilisez Scrutor pour enregistrer votre service et son décorateur dans Startup.cs
ou partout où vous configurez vos services :
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 }
Cette configuration utilise Scrutor pour envelopper l'enregistrement IService
avec LoggingDecorator
. Lors de la résolution IService
, le conteneur DI fournira une instance de LoggingDecorator
, qui à son tour encapsule une instance de MyService
. Cette approche permet de séparer les préoccupations en gardant la logique de journalisation en dehors de votre logique métier.
Lorsque nous construisons des systèmes complexes, il arrive souvent que nous souhaitions introduire quelque chose comme des indicateurs de fonctionnalités. Ceux-ci nous permettent d'emprunter différents chemins de code en fonction de la configuration et de ne pas avoir à recompiler et déployer une application entière. Dans certaines situations, il ne s'agit pas seulement de choisir un chemin de code différent, il s'agit d'échanger une implémentation entière de quelque chose !
Essayons ensemble un exemple ! Supposons que vous disposez d’une interface IFeatureService
avec plusieurs implémentations : NewFeatureService
(une nouvelle fonctionnalité expérimentale) et StandardFeatureService
(la fonctionnalité actuelle et stable). Vous souhaitez basculer entre ces implémentations au moment de l'exécution en fonction d'un indicateur de configuration.
Tout d’abord, définissez l’interface IFeatureService
et ses implémentations :
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 } }
Ensuite, vous avez besoin d'un moyen de déterminer quelle implémentation utiliser en fonction d'un paramètre de basculement de fonctionnalité. Il peut s'agir d'une valeur dans appsettings.json
, d'un paramètre de base de données ou de toute autre source de configuration.
Maintenant, utilisez Scrutor pour enregistrer dynamiquement l'implémentation de service appropriée en fonction de la bascule de fonctionnalité :
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 }
Adopter une approche comme celle-ci peut nous aider à :
Dans cet article, je vous ai fourni 3 conseils simples pour l'injection de dépendances avec Scrutor en C#. Après un bref aperçu de ce qu'est l'injection de dépendances et de la manière dont Scrutor s'y intègre, nous sommes passés directement à nos exemples. Nous avons pu voir l'analyse des assemblys, comment décorer les dépendances et même comment envisager de signaler des fonctionnalités d'implémentations entières !
L'injection de dépendances est quelque chose qui peut grandement simplifier vos applications à mesure qu'elles deviennent de plus en plus complexes, et Scrutor peut être d'une grande aide si vous souhaitez vous en tenir à l' offre IServiceCollection intégrée de Microsoft . Si vous avez trouvé cela utile et que vous recherchez davantage d'opportunités d'apprentissage, pensez à vous abonner à ma newsletter hebdomadaire gratuite sur le génie logiciel et regardez mes vidéos gratuites sur YouTube ! Rejoignez moi et les autres membres de la communauté sur Discord !
Également publié ici.