Die Abhängigkeitsinjektion in C# ist ein Lebensretter beim Organisieren von Abhängigkeiten – insbesondere in komplexeren ASP.NET Core-Anwendungen. Und wenn Sie bereits mit IServiceCollection vertraut sind oder sich einfach so nah wie möglich an die bereits bereitgestellten DI-Angebote halten möchten, ist Scrutor in C# eine großartige Erweiterung.
In diesem Artikel gebe ich Ihnen einen allgemeinen Überblick über Dependency Injection und Scrutor in C#. Aber von dort aus kommen wir direkt zu drei Tipps, die Sie mit Scrutor verwenden können und die sich bei Ihrer Bewerbung als sehr hilfreich erweisen könnten!
Scrutor ist ein leistungsstarkes NuGet-Paket in C#, das die Abhängigkeitsinjektion verbessert. Es vereinfacht die Registrierung und Auflösung von Abhängigkeiten und erleichtert Ihnen die Verwaltung und Organisation Ihres Codes.
Abhängigkeitsinjektion ist ein beliebter Ansatz zur Konfiguration von Anwendungen, der eine lose Kopplung fördert und die Testbarkeit und Wartbarkeit verbessert. Dabei werden Abhängigkeiten in eine Klasse eingefügt, anstatt sie direkt innerhalb der Klasse zu erstellen. Dies bedeutet, dass die Verantwortung für die Erstellung der Abhängigkeit nicht bei der Klasse liegt, die sie benötigt, sondern bei jemandem in der Aufrufliste. Letztendlich wird dadurch fast die gesamte Abhängigkeitserstellung an den Einstiegspunkt einer Anwendung verlagert, was diese unhandlich macht. Dependency-Injection-Frameworks helfen jedoch dabei , die gesamte Logik zu bereinigen und zu organisieren .
Scrutor führt dieses Konzept noch einen Schritt weiter, indem es eine einfache und intuitive Möglichkeit bietet, Abhängigkeiten zu registrieren und aufzulösen. Mit Scrutor müssen Sie nicht mehr jede Abhängigkeit einzeln manuell registrieren. Stattdessen können Sie Konventionen und Attribute verwenden, um den Prozess zu automatisieren.
Um zu veranschaulichen, wie Scrutor in C# zur Registrierung und Auflösung verwendet wird, betrachten wir ein einfaches Szenario. Stellen Sie sich vor, wir haben eine Anwendung, die die Verwendung verschiedener Datenrepositorys erfordert, wie z. B. UserRepository und ProductRepostiory.
Zuerst müssen wir das Scrutor NuGet-Paket in unserem Projekt installieren. Öffnen Sie die Package Manager-Konsole und führen Sie den folgenden Befehl aus:
Install-Package Scrutor
Als nächstes müssen wir unsere Repositorys und ihre entsprechenden Schnittstellen definieren. Dieses Beispiel ist im Wesentlichen leer, aber wir werden uns gleich auf diese beziehen:
public interface IUserRepository { // Interface methods } public class UserRepository : IUserRepository { // Implementation } public interface IProductRepository { // Interface methods } public class ProductRepository : IProductRepository { // Implementation }
Lassen Sie uns nun unsere Abhängigkeiten mit Scrutor verknüpfen. Fügen Sie in Ihrem Startcode (z. B. in der Methode „ConfigureServices“ in ASP.NET Core) den folgenden Code hinzu:
services.Scan(scan => scan .FromAssemblyOf<Startup>() .AddClasses(classes => classes.AssignableToAny( typeof(IUserRepository), typeof(IProductRepository))) .AsImplementedInterfaces() .WithScopedLifetime());
Dieser Code verwendet die Scan-Methode von Scrutor, um die Assembly zu scannen, die die Startup-Klasse enthält. Anschließend werden die Klassen herausgefiltert, die den Schnittstellen IUserRepository oder IProductRepository zuweisbar sind. Schließlich ordnet es die Implementierungen dieser Schnittstellen zu und registriert sie bei ihren jeweiligen Schnittstellen.
Jetzt können wir diese Abhängigkeiten in unsere Klassen einfügen. Nehmen wir zum Beispiel an, wir haben eine UserService-Klasse, die IUserRepository erfordert:
public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } // Rest of the class implementation }
Durch die Deklaration von IUserRepository als Abhängigkeit im Konstruktor löst Scrutor zusammen mit IServiceCollection die UserRepository-Instanz automatisch auf und fügt sie für uns ein.
Stellen Sie sich vor, Sie haben eine Serviceschnittstelle IService
und eine Implementierung MyService
. Sie möchten den Ein- und Ausgang jedes Methodenaufrufs in MyService
protokollieren, ohne dessen Geschäftslogik durch Protokollierungsprobleme zu beeinträchtigen.
Definieren Sie zunächst die IService
Schnittstelle und ihre Implementierung:
public interface IService { void DoWork(); } public class MyService : IService { public void DoWork() { // Business logic here } }
Als Nächstes erstellen Sie eine Dekoratorklasse LoggingDecorator
, die IService
implementiert. Diese Klasse umschließt den eigentlichen Dienst und fügt eine Protokollierung um die delegierten Methodenaufrufe hinzu:
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."); } }
Verwenden Sie jetzt Scrutor, um Ihren Dienst und seinen Dekorator in Startup.cs
oder wo immer Sie Ihre Dienste konfigurieren, zu registrieren:
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 }
Dieses Setup verwendet Scrutor, um die IService
Registrierung mit dem LoggingDecorator
zu umschließen. Beim Auflösen von IService
stellt der DI-Container eine Instanz von LoggingDecorator
bereit, die wiederum eine Instanz von MyService
umschließt. Dieser Ansatz erreicht die Trennung von Belangen, indem er die Protokollierungslogik außerhalb Ihrer Geschäftslogik hält.
Wenn wir komplexe Systeme erstellen, gibt es oft Fälle, in denen wir so etwas wie Feature-Flags einführen möchten. Dadurch können wir je nach Konfiguration unterschiedliche Codepfade verwenden und müssen nicht eine gesamte Anwendung neu kompilieren und bereitstellen. In manchen Situationen geht es nicht nur darum, einen anderen Codepfad auszuwählen, sondern auch darum, eine gesamte Implementierung von etwas auszutauschen!
Probieren wir gemeinsam ein Beispiel aus! Angenommen, Sie verfügen über eine IFeatureService
Schnittstelle mit mehreren Implementierungen: NewFeatureService
(eine neue, experimentelle Funktion) und StandardFeatureService
(die aktuelle, stabile Funktion). Sie möchten zur Laufzeit basierend auf einem Konfigurationsflag zwischen diesen Implementierungen wechseln.
Definieren Sie zunächst die IFeatureService
Schnittstelle und ihre Implementierungen:
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 } }
Als Nächstes benötigen Sie eine Möglichkeit, anhand einer Funktionsumschalteinstellung zu bestimmen, welche Implementierung verwendet werden soll. Dies kann ein Wert in appsettings.json
, eine Datenbankeinstellung oder eine andere Konfigurationsquelle sein.
Verwenden Sie nun Scrutor, um die entsprechende Dienstimplementierung basierend auf der Funktionsumschaltung dynamisch zu registrieren:
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 }
Ein Ansatz wie dieser kann uns helfen bei:
In diesem Artikel habe ich Ihnen drei einfache Tipps für die Abhängigkeitsinjektion mit Scrutor in C# gegeben. Nach einem kurzen Überblick darüber, was Abhängigkeitsinjektion ist und wie Scrutor darin passt, sind wir direkt zu unseren Beispielen übergegangen. Wir haben gesehen, wie man Assemblys scannt, wie man Abhängigkeiten dekoriert und sogar, wie man Feature-Flags für ganze Implementierungen in Betracht zieht!
Die Abhängigkeitsinjektion kann Ihre Anwendungen erheblich vereinfachen, wenn sie immer komplexer werden. Scrutor kann eine große Hilfe sein, wenn Sie beim integrierten IServiceCollection-Angebot von Microsoft bleiben möchten. Wenn Sie dies nützlich fanden und nach weiteren Lernmöglichkeiten suchen, sollten Sie meinen kostenlosen wöchentlichen Software-Engineering-Newsletter abonnieren und sich meine kostenlosen Videos auf YouTube ansehen! Treten Sie mir und den anderen Community-Mitgliedern auf Discord bei!
Auch hier veröffentlicht.