3

Ich möchte einen Dienst basierend auf dem HTTP-Header-Wert injizieren. Also habe ich 2 Klassen - DbDataProvider und InMemDataProvider, beide sind von IDataProvider implementiert. Immer wenn ein API-Aufruf erfolgt, wird vom Client ein Header übergeben, der festlegt, ob DbDataProvider erforderlich oder InMemDataProvider erforderlich ist. Wie erreiche ich das? Kurz gesagt, ich muss Service in der ServiceCollection in einem der Middlewares injizieren. Ist das möglich?In Dienst in Middleware in ASP.NET-Kern injizieren

Das Problem ist, dass in der ConfigureService-Methode in der Startup-Klasse ich den HttpContext nicht erhalten kann. Ich habe eine Middleware geschrieben, mit der ich den HTTP-Kontext bekommen kann, aber wie injiziere ich dort einen Dienst?

+1

Verhindern Sie das Injizieren von Diensten auf der Grundlage von Laufzeitbedingungen, so wie Sie [Laufzeitdaten selbst nicht einfügen] (https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99) in Ihre Anwendung einspeisen sollten Komponenten. Stattdessen muss eine zusammengesetzte Komponente in beide Implementierungen eingefügt werden und den Aufruf zur Laufzeit an die Implementierung weiterleiten. Fabriken sind kaum die richtige Lösung. – Steven

Antwort

4

Es gibt keinen einfachen oder sauberen Weg, dies zu tun. Sie können die IServiceCollection außerhalb der ConfigureServices Methode nicht ändern. Aber selbst wenn Sie könnten, ist es nutzlos, weil der Container bereits gebaut wurde, bevor aufgerufen wird.

Sie können eine Factory-Klasse erstellen und sie als Bereich registrieren.

public interface IDataProviderFactory 
{ 
    bool UseInMemoryProvider { get; set; } 
    IDataProvider Create(); 
} 

public class DataProviderFactory : IDataProviderFactory 
{ 
    private readonly IServiceProvider provider; 

    public bool UseInMemoryProvider { get; set; } 

    public DataProviderFactory(IServiceProvider provider) 
    { 
     this.provider = provider; 
    } 

    public IDataProvider Create() 
    { 
     if(UseInMemoryProvider) 
     { 
      return provider.RequestService<InMemoryDataProvider>(); 
     } 

     return provider.RequestService<DbDataProvider>(); 
    } 
} 

Dann in Ihrer Middleware:

public class MyMiddleware 
{ 
    public void Invoke(HttpContext context) 
    { 
     var dataProviderFactory = context.RequestServices.RequestService<IDataProviderFactory>(); 

     // Your logic here 
     if(...) 
     { 
      dataProviderFactory.UseInMemoryStore = true; 
     } 
    } 
} 

und in dem Controller/Dienstleistungen:

public class MyController : Controller 
{ 
    private readonly IDataProvider dataProvider; 

    public MyController(IDataProviderFactory dataProviderFactory) 
    { 
     dataProvider = dataProviderFactory.Create(); 
    } 
} 
1

Die Antwort oben von Tsen ist richtig. Sie sollten eine Fabrik implementieren.

Zusätzlich können Sie auch Factory-Methoden für die Servicesammlung registrieren. Wie folgt:

Services.AddTransient(serviceProvider => serviceProvider.GetService<IDataProviderFactory>().Create()) 

Dies registriert Ihren IDataProvider. In dem Create sollten Sie diesen HTTP-Header-Wert auswerten, damit es die richtige IDataProvider-Instanz zurückgibt. In jeder Klasse, die Sie benötigen, können Sie IDataProvider einfach über den Konstruktor anfordern, und die korrekte Implementierung wird vom Container bereitgestellt.

+0

Dies führt dazu, dass immer die gleiche Instanz zurückgegeben wird, da es ein Singleton ist;) Ein weiteres Problem an dieser Stelle ist, dass Sie keinen direkten Zugriff auf 'HttpContext' haben und' IHttpContextAccessor' aus Performancegründen nicht standardmäßig registriert ist), da das Erstellen bei jeder Anfrage einen geringen Overhead hat und wenn seine anderen Dienste HttpContext nicht außerhalb eines Controllers benötigen, sollte er es nicht nur aus diesem einen Grund registrieren. https://github.com/aspnet/Announcements/issues/190 – Tseng

+0

Ich meine transient (aktualisierte Antwort). Und wir haben IHttpContextAccessor tatsächlich manuell registriert. Jedem ihren eigenen würde ich sagen. –