2016-05-12 19 views
26

A questo punto sto iniettando cose nei miei controller con facilità, in alcuni casi creando la mia classe ResolverServices. La vita è buona.Iniezione dipendenza con classi diverse da una classe controller

Quello che non riesco a capire come fare è far sì che il framework si inietti automaticamente nelle classi non controller. Ciò che funziona è avere il quadro iniettare automaticamente nel mio controller IOptions, che è effettivamente la configurazione per il mio progetto:

public class MessageCenterController : Controller 
{ 
    private readonly MyOptions _options; 

    public MessageCenterController(IOptions<MyOptions> options) 
    { 
     _options = options.Value; 
    } 
} 

Sono pensare che io possa avere lo stesso accada per le mie classi di un presumo che sono chiudere quando ho imitare il controllore, in questo modo:

public class MyHelper 
{ 
    private readonly ProfileOptions _options; 

    public MyHelper(IOptions<ProfileOptions> options) 
    { 
     _options = options.Value; 
    } 

    public bool CheckIt() 
    { 
     return _options.SomeBoolValue; 
    } 
} 

Penso dove sto in mancanza è quando lo chiamo così:

public void DoSomething() 
{ 
    var helper = new MyHelper(??????); 

    if (helper.CheckIt()) 
    { 
     // Do Something 
    } 
} 

il proble m Ho rintracciato questo è praticamente tutto ciò che parla di DI sta parlando di esso a livello di controller. Ho provato a cercare dove avviene nel codice sorgente dell'oggetto Controller, ma è piuttosto pazzesco.

So che posso creare manualmente un'istanza di IOptions e passarlo al costruttore MyHelper, ma sembra che dovrei essere in grado di far funzionare il framework dal momento che funziona per Controllers.

Il vostro aiuto è apprezzato.

+3

Quando si utilizza l'iniezione di dipendenza non si chiama 'new'. Mai per oggetti che dovrebbero essere risolti – Tseng

+0

Quando sto provando a creare un'istanza di MyHelper, non chiamo nuovo? (1) Sembra troppo facile, (2) Si tratta di un errore di sintassi. :-) –

+0

Sì, questo è il punto principale dell'iniezione di dipendenza (soprattutto se si utilizza l'inversione dei contenitori di controllo che gestiscono e eseguono questa istanza). Per trasferire l'istanza al di fuori dei propri servizi/classi fino al punto in cui il contenitore di ioc esegue internamente questo. Nei casi in cui non è possibile iniettarlo tramite costruttore, creare una fabbrica e passare l'interfaccia di fabbrica al proprio servizio. l'implementazione di esso utilizza il contenitore per risolverlo, in caso di ASP.NET Core injecting 'IServiceProvider' nella tua factory e chiamando' IMyHelper helper = services.RequestService () ' – Tseng

risposta

7

Di seguito è riportato un esempio pratico di utilizzo di DI senza nulla che coinvolga controller MVC. Questo è quello che dovevo fare per capire il processo, quindi forse aiuterà qualcun altro.

L'oggetto ShoppingCart ottiene, tramite DI, un'istanza di INotifier (che comunica al cliente del loro ordine.)

using Microsoft.Extensions.DependencyInjection; 
using System; 

namespace DiSample 
{ 
    // STEP 1: Define an interface. 
    /// <summary> 
    /// Defines how a user is notified. 
    /// </summary> 
    public interface INotifier 
    { 
     void Send(string from, string to, string subject, string body); 
    } 

    // STEP 2: Implement the interface 
    /// <summary> 
    /// Implementation of INotifier that notifies users by email. 
    /// </summary> 
    public class EmailNotifier : INotifier 
    { 
     public void Send(string from, string to, string subject, string body) 
     { 
      // TODO: Connect to something that will send an email. 
     } 
    } 

    // STEP 3: Create a class that requires an implementation of the interface. 
    public class ShoppingCart 
    { 
     INotifier _notifier; 

     public ShoppingCart(INotifier notifier) 
     { 
      _notifier = notifier; 
     } 

     public void PlaceOrder(string customerEmail, string orderInfo) 
     { 
      _notifier.Send("[email protected]", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}"); 
     } 

    } 

    public class Program 
    { 
     // STEP 4: Create console app to setup DI 
     static void Main(string[] args) 
     { 
      // create service collection 
      var serviceCollection = new ServiceCollection(); 

      // ConfigureServices(serviceCollection) 
      serviceCollection.AddTransient<INotifier, EmailNotifier>(); 

      // create service provider 
      var serviceProvider = serviceCollection.BuildServiceProvider(); 

      // This is where DI magic happens: 
      var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider); 

      myCart.PlaceOrder("[email protected]", "2 Widgets"); 

      System.Console.Write("Press any key to end."); 
      System.Console.ReadLine(); 
     } 
    } 
} 
+4

E se volessi istanziare il 'ShoppingCart' in un'altra classe o metodo che non accediamo all'oggetto' serviceProvider'? – rejnev

+0

avendo lo stesso problema qui – Casey

+0

Grazie, ho dovuto cercare troppo largo per arrivare a ActivatorUtilities.CreateInstance. –

13

Diciamo che MyHelper è utilizzato da MyService che a sua volta viene utilizzato dal controller.

Il modo per risolvere questa situazione è:

  • Register sia MyService e MyHelper in Startup.ConfigureServices.

    services.AddTransient<MyService>(); 
    services.AddTransient<MyHelper>(); 
    
  • Il controllore riceve un'istanza di MyService nel suo costruttore.

    public HomeController(MyService service) { ... } 
    
  • MyService costruttore a sua volta riceverà un'istanza di MyHelper.

    public MyService(MyHelper helper) { ... } 
    

Il quadro DI potrà risolvere l'intero grafico oggetto senza problemi. Se sei preoccupato che vengano create nuove istanze ogni volta che viene risolto un oggetto, puoi leggere le diverse lifetime and registration options come il singleton o la durata della richiesta.

Si dovrebbe essere davvero sospettosi quando si pensa di dover creare manualmente un'istanza di qualche servizio, come si potrebbe finire nel service locator anti-pattern. Meglio lasciare la creazione degli oggetti nel contenitore DI. Se ti trovi veramente in quella situazione (diciamo che crei una fabbrica astratta), puoi utilizzare direttamente lo IServiceProvider (o richiedere un numero IServiceProvider nel costruttore o utilizzare quello exposed in the httpContext).

var foo = serviceProvider.GetRequiredService<MyHelper>(); 

Mi raccomando di leggere la specifica documentation sul quadro DI ASP.Net 5 e circa l'iniezione di dipendenza in generale.

+0

Il problema che ho con questo è che utilizzo i servizi in background che interagiscono con il database, quindi la durata dell'ambito con dbcontext non funziona, giusto? Come si utilizza DI in modo appropriato con i servizi di base e di base EF? – zuckerthoben

+0

grazie, questo funziona perfettamente per il mio scenario –

3

Purtroppo non esiste un modo diretto. L'unico modo sono riuscito a farlo funzionare è attraverso la creazione di una classe statica e l'utilizzo che in ogni altro, come di seguito:

public static class SiteUtils 
{ 

public static string AppName { get; set; } 

    public static string strConnection { get; set; } 

} 

Poi nella classe di avvio, compilarlo come di seguito:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
{ 
    //normal as detauls , removed for space 
    // set my variables all over the site 

    SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection"); 
    SiteUtils.AppName = Configuration.GetValue<string>("AppName"); 
} 

Anche se questo è un cattivo schema, in quanto ciò rimarrà per l'intero ciclo di vita dell'applicazione e non potrei trovare il modo migliore per usarlo al di fuori del controller.