6

Gibt es eine Möglichkeit, IServiceProvider.GetService<T> eine Instanz zurückzugeben, auch wenn T nicht explizit mit dem Container registriert ist?Wie wird der nicht registrierte Typ mit der Standard-MVC-Core-Abhängigkeitsinjektion aufgelöst

Wenn ich weiß, dass T Abhängigkeiten hat Ich möchte, dass sie basierend auf ihren Registrierungen injiziert werden, ohne T selbst registrieren zu müssen.

Ich glaube Ninject wird intelligent den am besten geeigneten Konstruktor oder Fallback zu einem parameterlosen Konstruktor ausarbeiten, wenn kein geeigneter Konstruktor gefunden wird. Ich möchte dieses Verhalten möglichst mit dem Standard-MVC-Core-DI-Framework replizieren.

+0

@Rafal https://docs.asp.net/en/latest/fundamentals/dependency-injection.html ich die Frage zu MVC6 für Klarheit aktualisiert haben –

Antwort

1

Gibt es eine Möglichkeit, IServiceProvider.GetService eine Instanz zurückzugeben, auch wenn T nicht explizit mit dem Container registriert ist?

Nr

Sie könnten Ihre eigene Konvention basierte Registrierung (es ist ziemlich einfach mit Reflexion und Erweiterungsmethoden) erstellen oder einen 3rd-Party-DI-Container verwenden, die eine eigene Konvention basierte Registrierung hat, aber es gibt nichts eingebaut. Auch AFAIK gibt es keine DI-Container, mit denen Sie Instanzen erstellen können, ohne sie zuerst zu registrieren, obwohl einige von ihnen automatisch bekannte Typen wie MVC-Controller und andere automatisch registrierte Typen als self registrieren, so dass Sie sie lösen können automatisch mit ihrem konkreten Typ.

Ich glaube Ninject wird intelligent den am besten geeigneten Konstruktor oder Fallback zu einem parameterlosen Konstruktor ausarbeiten, wenn kein geeigneter Konstruktor gefunden wird. Ich möchte dieses Verhalten mit dem Standard-MVC6-DI-Framework nach Möglichkeit replizieren.

Das hat wirklich nichts mit Ihrer Frage zu tun, obwohl es ein weiterer Schritt in die falsche Richtung ist. Siehe Dependency Injection anti-pattern: multiple constructors.

+0

dachte ich Ninject des 'Get' bietet„, wie er selbst "Verhalten für nicht registrierte Typen? Das meinte ich mit dem Ninject-Paragraphen. Ich könnte die Schaffung einer "GetServiceOrSelf" -Erweiterung für "IServiceProvider" –

+0

erkunden. Zu welchem ​​Zweck? Das bedeutet, dass Sie den Container als [Service Locator] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) verwenden, was Sie nicht tun sollten. Injizieren Sie Ihre Dienste durch Ihre Konstruktoren, injizieren Sie nicht 'IServiceProvider'. Und erstellen Sie eine Convention-basierte Registrierung, um die Wartung zu vereinfachen (oder alternativ einen Container von Drittanbietern zu verwenden, der eine Convention-basierte Registrierung hat). Aber letztendlich * mach * eine Containerregistrierung oder du wirst nicht in der Lage sein, die Registrierung einfach zu ändern oder die Lebensdauern der Instanzen zu kontrollieren, was der ganze Punkt von DI ist. – NightOwl888

+0

Der Aufruf von 'GetService' befindet sich im Container, ich übergebe' IServiceProvider' nicht. Ich suche nur nach einer bequemen Möglichkeit, sich auf das DI-Framework zu stützen, um eine Instanz einer Konfigurationsklasse zu erstellen. –

8

Um dies auf dem neuesten Stand zu halten: Es gibt eine Möglichkeit, genau das zu erreichen, wonach Sie suchen.

Standard-Abhängigkeit Injektor von .NET-Core

In meinem Beispiel erstelle ich eine neue Instanz von IServiceCollection für Vitrine:

var services = new ServiceCollection(); 
var serviceProvider = services.BuildServiceProvider(); 
var unregisteredClassInstance = 
    ActivatorUtilities.CreateInstance<UnregisteredClass>(serviceProvider); 

Dies gibt Ihnen eine Instanz des Typs UnregisteredClass, die vorher nicht war registriert, übernimmt jedoch Konstruktorparameter, die zuvor in der ServiceCollection registriert wurden.

Third Party Abhängigkeit Injektoren

SimpleInjector mit container.GetInstance<UnregisteredClass>(); das leicht zu tun.

+0

Ich liebe, wie sie dieses bekannte Unity "Feature" wegnahmen, um es durch einen anderen Hack zu ersetzen. – ADOConnection

1

Während standardmäßig keine Möglichkeit zur Auflösung nicht registrierter konkreter Typen zur Verfügung steht, können Sie eine Erweiterungsfunktion hinzufügen, die Ihnen die Möglichkeit gibt. Es ist wichtig zu beachten, dass dies als ein Service-Locator, der ein Anti-Pattern ist, missbraucht werden kann. Abhängig von Ihrer Verwendung ist es möglich, dies zu nutzen, ohne es in einen Service Locator zu verwandeln.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Text; 

namespace Microsoft.Extensions.DependencyInjection 
{ 
    public static class ServiceProviderExtensions 
    { 
     public static TService AsSelf<TService>(this IServiceProvider serviceProvider) 
     { 
      return (TService)AsSelf(serviceProvider, typeof(TService)); 
     } 
     public static object AsSelf(this IServiceProvider serviceProvider, Type serviceType) 
     { 
      var constructors = serviceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance) 
       .Select(o => o.GetParameters()) 
       .ToArray() 
       .OrderByDescending(o => o.Length) 
       .ToArray(); 

      if (!constructors.Any()) 
      { 
       return null; 
      } 

      object[] arguments = ResolveParameters(serviceProvider, constructors); 

      if (arguments == null) 
      { 
       return null; 
      } 

      return Activator.CreateInstance(serviceType, arguments); 
     } 

     private static object[] ResolveParameters(IServiceProvider resolver, ParameterInfo[][] constructors) 
     { 
      foreach (ParameterInfo[] constructor in constructors) 
      { 
       bool hasNull = false; 
       object[] values = new object[constructor.Length]; 
       for (int i = 0; i < constructor.Length; i++) 
       { 
        var value = resolver.GetService(constructor[i].ParameterType); 
        values[i] = value; 
        if (value == null) 
        { 
         hasNull = true; 
         break; 
        } 
       } 
       if (!hasNull) 
       { 
        // found a constructor we can create. 
        return values; 
       } 
      } 

      return null; 
     } 
    } 
}