An diesem Punkt füge ich mühelos Dinge in meine Controller ein und baue in einigen Fällen meine eigene ResolverServices-Klasse auf. Das Leben ist gut .
Was ich nicht herausfinden kann, ist, das Framework so zu gestalten, dass es automatisch in Nicht-Controller-Klassen eingefügt wird. Was funktioniert, ist, dass das Framework automatisch in meinen Controller eingefügt wird. Dies IOptions
ist effektiv die Konfiguration für mein Projekt:
public class MessageCenterController : Controller
{
private readonly MyOptions _options;
public MessageCenterController(IOptions<MyOptions> options)
{
_options = options.Value;
}
}
Ich überlege, ob ich das auch für meine eigenen Klassen tun kann. Ich gehe davon aus, dass ich nah dran bin, wenn ich den Controller nachahme:
public class MyHelper
{
private readonly ProfileOptions _options;
public MyHelper(IOptions<ProfileOptions> options)
{
_options = options.Value;
}
public bool CheckIt()
{
return _options.SomeBoolValue;
}
}
Ich denke, wo ich versage, ist, wenn ich es so nenne:
public void DoSomething()
{
var helper = new MyHelper(??????);
if (helper.CheckIt())
{
// Do Something
}
}
Das Problem, das ich habe, ist, dass praktisch alles, was über DI spricht, auf Controller-Ebene darüber spricht. Ich habe versucht herauszufinden, wo es im Controller
Objektquellcode passiert , aber es wird dort irgendwie verrückt.
Ich weiß, dass ich eine Instanz von IOptions manuell erstellen und an den MyHelper
Konstruktor übergeben kann, aber es scheint, als ob ich in der Lage sein sollte, das Framework dazu zu bringen, da es funktioniert Controllers
.
quelle
new
. Niemals für Objekte, die aufgelöst werden solltenIServiceProvider
in Ihrem Betrieb und ruftIMyHelper helper = services.RequestService<IMyHelper>()
Antworten:
Im Folgenden finden Sie ein funktionierendes Beispiel für die Verwendung von DI ohne MVC-Controller. Dies ist, was ich tun musste, um den Prozess zu verstehen, also hilft es vielleicht jemand anderem.
Das ShoppingCart-Objekt erhält über DI eine Instanz von INotifier (die den Kunden über ihre Bestellung benachrichtigt).
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(); } } }
quelle
ShoppingCart
in einer anderen Klasse oder Methode instanziieren möchte, dass wir nicht auf dasserviceProvider
Objekt zugreifen ?Angenommen, es
MyHelper
wird verwendet, vonMyService
dem wiederum Ihr Controller verwendet wird.Der Weg, um diese Situation zu lösen, ist:
Registrieren Sie sich beide
MyService
undMyHelper
inStartup.ConfigureServices
.Der Controller empfängt eine Instanz von
MyService
in seinem Konstruktor.public HomeController(MyService service) { ... }
MyService
Der Konstruktor erhält wiederum eine Instanz vonMyHelper
.public MyService(MyHelper helper) { ... }
Das DI-Framework kann den gesamten Objektgraphen problemlos auflösen. Wenn Sie befürchten, dass bei jeder Auflösung eines Objekts neue Instanzen erstellt werden, können Sie sich über die verschiedenen Lebensdauer- und Registrierungsoptionen wie Singleton oder Anforderungslebensdauer informieren .
Sie sollten wirklich misstrauisch sein, wenn Sie der Meinung sind, dass Sie eine Instanz eines Dienstes manuell erstellen müssen, da Sie möglicherweise im Anti-Pattern des Service Locator landen . Überlassen Sie das Erstellen der Objekte besser dem DI-Container. Wenn Sie sich wirklich in dieser Situation befinden (sagen wir, Sie erstellen eine abstrakte Factory), können Sie die
IServiceProvider
direkt verwenden (entweder eineIServiceProvider
in Ihrem Konstruktor anfordern oder die im httpContext bereitgestellte verwenden ).var foo = serviceProvider.GetRequiredService<MyHelper>();
Ich würde empfehlen, die spezifische Dokumentation zum ASP.Net 5 DI-Framework und zur Abhängigkeitsinjektion im Allgemeinen zu lesen .
quelle
Leider gibt es keinen direkten Weg. Die einzige Möglichkeit, es zum Laufen zu bringen, besteht darin, eine statische Klasse zu erstellen und diese überall wie folgt zu verwenden:
public static class SiteUtils { public static string AppName { get; set; } public static string strConnection { get; set; } }
Füllen Sie es dann in Ihrer Startklasse wie folgt aus:
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"); }
Obwohl dies ein schlechtes Muster ist, bleibt dies für den gesamten Lebenszyklus der Anwendung bestehen und ich konnte keinen besseren Weg finden, es außerhalb des Controllers zu verwenden.
quelle
Hier ist ein vollständigeres Beispiel, um die Frage des OP direkt zu beantworten, basierend auf der aktuellen .NET Core 2.2 DI-Dokumentation hier . Hinzufügen dieser Antwort, da sie möglicherweise jemandem hilft, der neu in .NET Core DI ist, und weil diese Frage das beste Suchergebnis von Google ist.
Fügen Sie zunächst eine Schnittstelle für MyHelper hinzu:
public interface IMyHelper { bool CheckIt(); }
Aktualisieren Sie zweitens die MyHelper-Klasse, um die Schnittstelle zu implementieren (drücken Sie in Visual Studio Strg-, um die Schnittstelle zu implementieren):
public class MyHelper : IMyHelper { private readonly ProfileOptions _options; public MyHelper(IOptions<ProfileOptions> options) { _options = options.Value; { public bool CheckIt() { return _options.SomeBoolValue; } }
Drittens registrieren Sie die Schnittstelle als vom Framework bereitgestellten Dienst im DI-Dienstcontainer. Registrieren Sie dazu den IMyHelper-Dienst mit dem konkreten Typ MyHelper in der ConfigureServices-Methode in Startup.cs.
public void ConfigureServices(IServiceCollection services) { ... services.AddScoped<IMyHelper, MyHelper>(); ... }
Viertens erstellen Sie eine private Variable, um auf eine Instanz des Dienstes zu verweisen. Übergeben Sie den Dienst als Argument im Konstruktor (über die Konstruktorinjektion) und initialisieren Sie die Variable mit der Dienstinstanz. Verweisen Sie über die private Variable auf Eigenschaften oder Aufrufmethoden für diese Instanz der benutzerdefinierten Klasse.
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 } } }
quelle
Sie können Activator.CreateInstance () verwenden. Hier ist eine Wrapper-Funktion dafür. Sie verwenden dies wie folgt.
var determiniertProgrammatisch = "My.NameSpace.DemoClass1"; // implementiert die IDemo-Schnittstelle var obj = CreateInstance <My.NameSpace.IDemo, string> (bestimmt programmgesteuert: "Dies geht in den Parameter des Konstruktors.", "Lassen Sie diesen Parameter weg, wenn Ihre Klasse in der aktuellen Assembly lebt");
Jetzt haben Sie eine Instanz von obj, die anhand des programmgesteuert bestimmten Typs instanziiert wird. Dieses Objekt kann in Nicht-Controller-Klassen eingefügt werden.
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: Sie können System.AppDomain.CurrentDomain.GetAssemblies () durchlaufen, um den Namen der Assembly zu ermitteln, in der sich Ihre Klasse befindet. Dieser Name wird im 3. Parameter der Wrapper-Funktion verwendet.
quelle