2016-05-12 11 views
26

W tym momencie z łatwością wprowadzam elementy do kontrolerów, w niektórych przypadkach budując własną klasę ResolverServices. Życie jest dobre.Wstrzykiwanie zależności z klasami innymi niż klasa kontrolera

Czego nie mogę się dowiedzieć, jak to zrobić, to uzyskać szkielet automatycznie wprowadzany do klas innych niż kontroler. Co robi praca jest posiadanie ramy automatycznie wprowadzić do mojego kontrolera IOptions, która jest skutecznie konfiguracja dla mojego projektu:

public class MessageCenterController : Controller 
{ 
    private readonly MyOptions _options; 

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

jestem, że mogę mieć to samo stało dla moich własnych klas Przypuszczam jestem zamknąć kiedy naśladować kontrolera, tak:

public class MyHelper 
{ 
    private readonly ProfileOptions _options; 

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

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

myślę gdzie mam braku kiedy nazywam to tak:

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

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

proble m Mam śledzenie tego jest praktycznie wszystkim, co mówi o DI mówi o tym na poziomie kontrolera. Próbowałem polować, gdzie to się dzieje w kodzie źródłowym obiektu Controller, ale robi się tam trochę szaleńczo.

Wiem, że mogę ręcznie utworzyć instancję IOptions i przekazać ją do konstruktora MyHelper, ale wygląda na to, że powinienem móc uzyskać framework, ponieważ działa on pod Controllers.

Twoja pomoc jest doceniana.

+3

Podczas korzystania z Dependency Injection nie nazywać 'new'. Nigdy dla obiektów, które powinny zostać rozwiązane. – Tseng

+0

Kiedy próbuję utworzyć instancję MyHelper, nie nazywam nowego? (1) To brzmi zbyt łatwo, (2) Jest to błąd składniowy. :-) –

+0

Tak, to jest cały punkt zastrzyku zależności (szczególnie jeśli używasz inwersji kontrolnych pojemników, które zarządzają i robią to). Aby przesunąć instancję poza twoje usługi/klasy do punktu, w którym kontener ioc robi to wewnętrznie. W przypadkach, w których nie można wstrzyknąć go za pomocą konstruktora, należy utworzyć fabrykę i przekazać interfejs fabryczny do usługi. jego implementacja wykorzystuje kontener do rozwiązania tego problemu, w przypadku ASP.NET Core wstrzykiwania 'IServiceProvider' w twoim zakładzie i wywoływania' IMyHelper helper = services.RequestService () ' – Tseng

Odpowiedz

7

Poniżej znajduje się przykład użycia DI bez niczego, co dotyczy kontrolerów MVC. Właśnie to musiałem zrobić, aby zrozumieć proces, więc może to pomoże komuś innemu.

Przedmiotem ShoppingCart dostaje za pośrednictwem DI instancji INotifier (który powiadamia klienta o ich kolejności.)

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

Co zrobić, jeśli chcę utworzyć instancję 'ShoppingCart' w innej klasie lub metodzie, do której nie mamy dostępu do obiektu' serviceProvider'? – rejnev

+0

o tym samym numerze tutaj – Casey

+0

Dziękuję, musiałem szukać zbyt daleko, aby dostać się do ActivatorUtilities.CreateInstance. –

13

Załóżmy, że MyHelper jest używane przez MyService, który z kolei jest używany przez kontroler.

Sposobem na rozwiązanie tej sytuacji jest:

  • Register zarówno MyService i MyHelper w Startup.ConfigureServices.

    services.AddTransient<MyService>(); 
    services.AddTransient<MyHelper>(); 
    
  • Sterownik odbiera wystąpienie MyService jej konstruktora.

    public HomeController(MyService service) { ... } 
    
  • MyService konstruktor z kolei otrzymują instancję MyHelper.

    public MyService(MyHelper helper) { ... } 
    

Ramy DI będzie w stanie rozwiązać cały wykres obiektu bez problemów. Jeśli martwisz się, że nowe instancje są tworzone za każdym razem, gdy obiekt jest rozwiązywany, możesz przeczytać o różnych lifetime and registration options podobnych do pojedynczych lub życiach żądań.

Powinieneś być naprawdę podejrzliwy, gdy myślisz, że musisz ręcznie utworzyć wystąpienie jakiejś usługi, ponieważ może to skończyć się w service locator anti-pattern. Lepiej pozostawić tworzenie obiektów w pojemniku DI. Jeśli naprawdę znajdziesz się w takiej sytuacji (załóżmy, że tworzysz abstrakcyjną fabrykę), możesz użyć bezpośrednio IServiceProvider (albo zażądaj IServiceProvider w swoim konstruktorze lub użyj jednego exposed in the httpContext).

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

Polecam czytanie specyficzną documentation o DI ram ASP.Net 5 i około wstrzykiwania zależności w ogóle.

+0

Problem z tym, że używam usług w tle które współdziałają z bazą danych, więc zakres życia z dbcontext nie działa, prawda? W jaki sposób prawidłowo używasz DI z usługami podstawowymi EF i usługami tła? – zuckerthoben

+0

thanx, działa to doskonale w moim scenariuszu –

3

Niestety nie ma bezpośredniej drogi. Jedynym sposobem udało mi się zrobić to praca jest stworzenie klasę statyczną i za pomocą że wszędzie indziej, jak poniżej:

public static class SiteUtils 
{ 

public static string AppName { get; set; } 

    public static string strConnection { get; set; } 

} 

potem w swojej klasie startowej, wypełnić go jak poniżej:

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"); 
} 

Chociaż to zły wzór, ponieważ pozostanie on przez cały cykl życia aplikacji i nie mogłem znaleźć lepszego sposobu na wykorzystanie go poza kontrolerem.

Powiązane problemy