W tym momencie z łatwością wstrzykuję rzeczy do moich kontrolerów, w niektórych przypadkach budując własną klasę ResolverServices. Życie jest dobre .
To, czego nie potrafię zrobić, to sprawić, by framework automatycznie wstawiał do klas niebędących kontrolerami. To, co działa, to automatyczne wstrzykiwanie frameworka do mojego kontrolera IOptions
, który jest w rzeczywistości konfiguracją dla mojego projektu:
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
public MessageCenterController(IOptions<MyOptions> options)
{
_options = options.Value;
}
}
Zastanawiam się, czy mogę zrobić to samo dla moich własnych zajęć. Zakładam, że jestem blisko, kiedy naśladuję kontroler, w ten sposób:
public class MyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
}
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
Myślę, że zawodzę, gdy nazywam to tak:
public void DoSomething()
{
var helper = new MyHelper(??????);
if (helper.CheckIt())
{
// Do Something
}
}
Problem, który mam ze śledzeniem tego, to praktycznie wszystko, co mówi o DI, mówi o tym na poziomie kontrolera. Próbowałem znaleźć miejsce, w którym to się dzieje w Controller
kodzie źródłowym obiektu, ale robi się tam trochę szalenie.
Wiem, że mogę ręcznie utworzyć wystąpienie IOptions i przekazać je MyHelper
konstruktorowi, ale wydaje się, że powinienem być w stanie to zrobić, ponieważ działa Controllers
.
źródło
new
. Nigdy dla obiektów, które powinny zostać rozwiązaneIServiceProvider
do fabryki i wywołanieIMyHelper helper = services.RequestService<IMyHelper>()
Odpowiedzi:
Poniżej znajduje się działający przykład użycia DI bez niczego, co dotyczy kontrolerów MVC. To właśnie musiałem zrobić, aby zrozumieć ten proces, więc może pomoże to komuś innemu.
Obiekt ShoppingCart pobiera przez DI instancję INotifier (która powiadamia klienta o jego zamówieniu).
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(); } } }
źródło
ShoppingCart
w innej klasie lub metodzie, do której nie mamy dostępu doserviceProvider
obiektu?Powiedzmy, że
MyHelper
jest używany przezMyService
który z kolei jest używany przez kontroler.Sposób rozwiązania tej sytuacji to:
Zarejestruj zarówno
MyService
iMyHelper
wStartup.ConfigureServices
.Kontroler otrzymuje wystąpienie
MyService
w swoim konstruktorze.public HomeController(MyService service) { ... }
MyService
konstruktor z kolei otrzyma wystąpienieMyHelper
.public MyService(MyHelper helper) { ... }
Framework DI będzie w stanie bez problemu rozwiązać cały graf obiektu. Jeśli martwisz się, że nowe instancje będą tworzone za każdym razem, gdy obiekt zostanie rozwiązany, możesz przeczytać o różnych okresach istnienia i opcjach rejestracji, takich jak okresy istnienia pojedynczych lub żądań.
Powinieneś być naprawdę podejrzliwy, gdy myślisz, że musisz ręcznie utworzyć wystąpienie jakiejś usługi, ponieważ możesz skończyć w anty-wzorca lokalizatora usług . Lepiej pozostaw tworzenie obiektów w DI Container. Jeśli naprawdę znajdziesz się w takiej sytuacji (powiedzmy, że tworzysz fabrykę abstrakcyjną), możesz użyć
IServiceProvider
bezpośrednio (albo zażądajIServiceProvider
w konstruktorze, albo użyj tego, który jest ujawniony w httpContext ).var foo = serviceProvider.GetRequiredService<MyHelper>();
Zalecałbym przeczytanie szczegółowej dokumentacji dotyczącej frameworka ASP.Net 5 DI i ogólnie o wstrzykiwaniu zależności.
źródło
Niestety nie ma bezpośredniego sposobu. Jedynym sposobem, w jaki udało mi się to zrobić, jest utworzenie statycznej klasy i użycie jej wszędzie indziej, jak poniżej:
public static class SiteUtils { public static string AppName { get; set; } public static string strConnection { get; set; } }
Następnie w swojej klasie startowej wypełnij to 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ż jest to zły wzorzec, ponieważ pozostanie przez cały cykl życia aplikacji i nie mogłem znaleźć lepszego sposobu na użycie go poza kontrolerem.
źródło
Oto bardziej kompletny przykład, aby bezpośrednio odpowiedzieć na pytanie OP, w oparciu o aktualną dokumentację .NET Core 2.2 DI tutaj . Dodanie tej odpowiedzi może pomóc komuś, kto jest nowy w .NET Core DI i ponieważ to pytanie jest najlepszym wynikiem wyszukiwania Google.
Najpierw dodaj interfejs dla MyHelper:
public interface IMyHelper { bool CheckIt(); }
Po drugie, zaktualizuj klasę MyHelper, aby zaimplementować interfejs (w programie Visual Studio naciśnij ctrl-., Aby zaimplementować interfejs):
public class MyHelper : IMyHelper { private readonly ProfileOptions _options; public MyHelper(IOptions<ProfileOptions> options) { _options = options.Value; { public bool CheckIt() { return _options.SomeBoolValue; } }
Po trzecie, zarejestruj interfejs jako usługę udostępnioną przez platformę w kontenerze usługi DI. Zrób to, rejestrując usługę IMyHelper z konkretnym typem MyHelper w metodzie ConfigureServices w Startup.cs.
public void ConfigureServices(IServiceCollection services) { ... services.AddScoped<IMyHelper, MyHelper>(); ... }
Po czwarte, utwórz zmienną prywatną, aby odwołać się do wystąpienia usługi. Przekaż usługę jako argument w konstruktorze (za pomocą iniekcji konstruktora), a następnie zainicjuj zmienną za pomocą wystąpienia usługi. Odwołaj się do wszelkich właściwości lub wywołaj metody w tym wystąpieniu klasy niestandardowej za pośrednictwem zmiennej prywatnej.
public class MessageCenterController : Controller { private readonly MyOptions _options; private readonly IMyHelper _myHelper; public MessageCenterController( IOptions<MyOptions> options, IMyHelper myHelper ) { _options = options.value; _myHelper = myHelper; } public void DoSomething() { if (_myHelper.CheckIt()) { // Do Something } } }
źródło
Możesz użyć Activator.CreateInstance (). Oto funkcja opakowująca. Sposób korzystania z tego jest następujący.
var determinedProgrammatically = "My.NameSpace.DemoClass1"; // implements IDemo interface var obj = CreateInstance<My.NameSpace.IDemo, string>(determinedProgrammatically, "This goes into the parameter of the constructor.", "Omit this parameter if your class lives in the current assembly");
Teraz masz wystąpienie obj, którego wystąpienie jest tworzone na podstawie typu określonego programowo. Ten obiekt można wstawić do klas niebędących kontrolerami.
public TInterface CreateInstance<TInterface, TParameter>(string typeName, TParameter constructorParam, string dllName = null) { var type = dllName == null ? System.Type.GetType(typeName) : System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.StartsWith(dllName, System.StringComparison.OrdinalIgnoreCase)).GetType(typeName); return (TInterface)System.Activator.CreateInstance(type, constructorParam); }
PS: Możesz iterować przez System.AppDomain.CurrentDomain.GetAssemblies (), aby określić nazwę zestawu, w którym znajduje się twoja klasa. Ta nazwa jest używana w trzecim parametrze funkcji opakowania.
źródło