2016-07-04 21 views
6

C'è un modo per ottenere IServiceProvider.GetService<T> per restituire un'istanza anche se non è registrato esplicitamente con il contenitore?Come risolvere il tipo non registrato utilizzando l'iniezione di dipendenza MVC Core standard

Se so che T ha dipendenze mi piacerebbe che fossero iniettati in base alle loro registrazioni senza dover registrare T stesso.

Credo che Ninject elabori in modo intelligente il costruttore o il fallback più appropriato a un costruttore senza parametri se non viene trovato un costruttore adatto. Mi piacerebbe replicare questo comportamento usando lo standard MVC Core DI se possibile.

+0

@Rafal https://docs.asp.net/en/latest/fundamentals/dependency-injection.html Ho aggiornato la questione per MVC6 per chiarezza –

risposta

1

C'è un modo per ottenere IServiceProvider.GetService per restituire un'istanza anche se T non è registrato esplicitamente con il contenitore?

No.

Si potrebbe creare la propria registrazione di convenzione-based (il suo piuttosto semplice utilizzando la riflessione e Metodi di estensione) o utilizzare un contenitore DI 3rd party che ha il proprio registrazione convenzione-based, ma non v'è niente built-in.

Anche AFAIK, non esistono contenitori DI che consentono di creare istanze senza prima registrarli, sebbene alcuni di essi registrino automaticamente tipi noti come controller MVC e altri tipi di registrazione automatica "come se stessi" in modo da poterli risolvere automaticamente usando il loro tipo concreto.

Credo che Ninject elabori in modo intelligente il costruttore o il fallback più appropriato a un costruttore senza parametri se non viene trovato un costruttore adatto. Mi piacerebbe replicare questo comportamento usando il framework standard MVC6 DI se possibile.

Questo in realtà non ha nulla a che fare con la tua domanda, anche se è un altro passo nella direzione sbagliata. Vedi Dependency Injection anti-pattern: multiple constructors.

+0

ho pensato Ninject del 'GET' fornisce "come auto "comportamento per tipi non registrati? Questo era quello che intendevo per il paragrafo di Ninject. Potrei esplorare la creazione di un'estensione 'GetServiceOrSelf' per' IServiceProvider' –

+0

A quale scopo? Ciò implica che stai usando il contenitore come [locator di servizio] (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/), che non dovresti fare. Inietti i tuoi servizi attraverso i tuoi costruttori, non inserire 'IServiceProvider'. E creare una registrazione basata sulla convenzione per semplificare la manutenzione (o in alternativa utilizzare un contenitore di terze parti con registrazione basata sulla convenzione). Ma alla fine * do * fornire una registrazione del contenitore o non sarà in grado di * modificare * la registrazione in giro facilmente o controllare le durate delle istanze, che è l'intero punto di DI. – NightOwl888

+0

La chiamata a 'GetService' è nel contenitore, non sto passando' IServiceProvider' in giro. Sto solo cercando un modo conveniente per appoggiare il framework DI per creare un'istanza di una classe di configurazione. –

8

Per tenerlo aggiornato: esiste un modo per ottenere esattamente ciò che si sta cercando.

standard dipendenza iniettore di .NET Nucleo

Nel mio esempio ho creare una nuova istanza di IServiceCollection per la vetrina:

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

Questo vi dà un esempio del tipo di UnregisteredClass che non era in precedenza registrato, ma prende i parametri del costruttore che sono registrati precedentemente su ServiceCollection.

di terzi iniettori di dipendenza

SimpleInjector possono farlo facilmente con container.GetInstance<UnregisteredClass>();.

+0

Mi piace come hanno portato via questa caratteristica "Unity" ben conosciuta per sostituirla con un altro hack. – ADOConnection

1

Anche se non è previsto in modo predefinito la risoluzione dei tipi di calcestruzzo non registrati, è possibile aggiungere una funzione di estensione che darà la possibilità. È importante notare che questo può essere abusato come un localizzatore di servizi che è un Anti-pattern. A seconda dell'utilizzo è possibile sfruttarlo senza trasformarlo in un localizzatore di servizi.

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; 
     } 
    } 
} 
Problemi correlati