it-swarm.com.de

Wie kann ich einen Laufzeitparameter als Teil der Abhängigkeitsauflösung übergeben?

Ich muss in der Lage sein, eine Verbindungszeichenfolge an einige meiner Service-Implementierungen zu übergeben. Ich mache das im Konstruktor. Die Verbindungszeichenfolge kann vom Benutzer konfiguriert werden. Der ClaimsPrincipal wird als Claim hinzugefügt.

Alles in Ordnung soweit.

Leider möchte ich auch in der Lage sein, die Abhängigkeitsinjektionsfunktionen in ASP.NET Core in vollem Umfang zu nutzen und die Dienstimplementierung über DI aufzulösen.

Ich habe eine POC-Implementierung:

public interface IRootService
{
    INestedService NestedService { get; set; }

    void DoSomething();
}

public class RootService : IRootService
{
    public INestedService NestedService { get; set; }

    public RootService(INestedService nestedService)
    {
        NestedService = nestedService;
    }

    public void DoSomething()
    {
        // implement
    }
}


public interface INestedService
{
    string ConnectionString { get; set; }

    void DoSomethingElse();
}

public class NestedService : INestedService
{
    public string ConnectionString { get; set; }

    public NestedService(string connectionString)
    {
        ConnectionString = connectionString;
    }

    public void DoSomethingElse()
    {
        // implement
    }
}

Diese Dienste wurden beim Start registriert und INestedService wurde dem Konstruktor eines Controllers hinzugefügt.

public HomeController(INestedService nestedService)
{
    NestedService = nestedService;
}

Wie erwartet erhalte ich den Fehler Unable to resolve service for type 'System.String' while attempting to activate 'Test.Dependency.Services.NestedService'.

Welche Möglichkeiten habe ich hier?

25
Ant Swift

Einfache Konfiguration

public void ConfigureServices(IServiceCollection services)
{
    // Choose Scope, Singleton or Transient method
    services.AddSingleton<IRootService, RootService>();
    services.AddSingleton<INestedService, NestedService>(serviceProvider=>
    {
         return new NestedService("someConnectionString");
    });
}

Mit appSettings.json

Wenn Sie beschließen, Ihre Verbindungszeichenfolge in appSettings.json zu verbergen, z.

"Data": {
  "ConnectionString": "someConnectionString"
}

Vorausgesetzt, Sie haben Ihre appSettings.json in den ConfigurationBuilder geladen (normalerweise im Konstruktor der Startup-Klasse), dann würden Ihre ConfigureServices folgendermaßen aussehen:

public void ConfigureServices(IServiceCollection services)
{
    // Choose Scope, Singleton or Transient method
    services.AddSingleton<IRootService, RootService>();
    services.AddSingleton<INestedService, NestedService>(serviceProvider=>
    {
         var connectionString = Configuration["Data:ConnectionString"];
         return new NestedService(connectionString);
    });
}

Mit Erweiterungsmethoden

namespace Microsoft.Extensions.DependencyInjection
{
    public static class RootServiceExtensions //you can pick a better name
    {
        //again pick a better name
        public static IServiceCollection AddRootServices(this IServiceCollection services, string connectionString) 
        {
            // Choose Scope, Singleton or Transient method
            services.AddSingleton<IRootService, RootService>();
            services.AddSingleton<INestedService, NestedService>(_ => 
              new NestedService(connectionString));
        }
    }
}

Dann würde Ihre ConfigureServices-Methode so aussehen

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration["Data:ConnectionString"];
    services.AddRootServices(connectionString);
}

Mit dem Options Builder

Wenn Sie weitere Parameter benötigen, können Sie einen Schritt weiter gehen und eine Optionsklasse erstellen, die Sie an den Konstruktor von RootService übergeben. Wenn es komplex wird, können Sie das Builder-Muster verwenden.

35
Ivan Prodanov

Um Laufzeitparameter zu übergeben, die beim Start der Anwendung nicht bekannt sind, müssen Sie das Factory-Pattern verwenden. Hier haben Sie zwei Möglichkeiten

  1. fabrik methode

    services.AddTransient<Func<string,INestedService>>((provider) => 
    {
        return new Func<string,INestedService>( 
            (connectionString) => new NestedService(connectionString)
        );
    });
    

    und fügen Sie die Factory-Methode anstelle von INestedService in Ihren Dienst ein.

    public class RootService : IRootService
    {
        public INestedService NestedService { get; set; }
    
        public RootService(Func<string,INestedService> nestedServiceFactory)
        {
            NestedService = nestedServiceFactory("ConnectionStringHere");
        }
    
        public void DoSomething()
        {
            // implement
        }
    }
    

    oder lösen Sie es per Anruf

    public class RootService : IRootService
    {
        public Func<string,INestedService> NestedServiceFactory { get; set; }
    
        public RootService(Func<string,INestedService> nestedServiceFactory)
        {
            NestedServiceFactory = nestedServiceFactory;
        }
    
        public void DoSomething(string connectionString)
        {
            var nestedService = nestedServiceFactory(connectionString);
    
            // implement
        }
    }
    
  2. fabrikklasse

    public class RootServiceFactory : IRootServiceFactory 
    {
        // in case you need other dependencies, that can be resolved by DI
        private readonly IServiceCollection services;
    
        public RootServiceCollection(IServiceCollection services)
        {
            this.services = services;
        }
    
        public CreateInstance(string connectionString) 
        {
            // instantiate service that needs runtime parameter
            var nestedService = new NestedService(connectionString);
    
            // resolve another service that doesn't need runtime parameter
            var otherDependency = services.GetService<IOtherService>()
    
            // pass both into the RootService constructor and return it
            return new RootService(otherDependency, nestedDependency);
        }
    }
    

    und injiziere IRootServiceFactory anstelle deines IRootService.

    IRootService rootService = rootServiceFactory.CreateIntsance(connectionString);
    
31
Tseng