Implizit bedeutet das der Erzeuger des Objektes per iocContainer.Resolve<> das Object mit seinen gesamten Abhängigkeiten erzeugt, und nicht das Object seine einzelnen Anhängigkeiten ( Services ) selber per iocContainer.Resolve<> holt.
Ein Objekt sollte nur dann iocContainer.Resolve<> kennen/benutzen, wenn es nicht anders geht ( z.b. Factory ).
Also nochmal iocContainer.Resolve<> wird immer bei der Erzeugung von Objekten gebraucht, aber im Normalfall muss nur eine ( explizite / implizite ) Factory überhaupt wissen das es einen Container gibt.
Genau, das was FZelle geschrieben hat geht in diese Richtung.
Ich versuche das mal einfach zu erklären.
Bei der impliziten Anwendung geht es darum, das du an einem Ort ein container.Resolve<IFoo>() drin hast. Das kannst du dir als Resolution-Root vorstellen, also die Wurzel, wo die Auflösung beginnt.
Wenn das jetzt nicht ein IFoo, sondern bspw. ein IController ist, enthält er und seine Objekte komplett alles, was nötig ist (Bei einem ASP.NET MVC Request).
Der Container löst nach diesem einen expliziten Aufruf dann implizit 1, oder soviele Abhängigkeiten und Unterabhängigkeiten auf, wie gefordert sind.
Aaaahh...JETZT hat es bei mir "Klick" gemacht.
Vielen Dank an euch alle!
Mir war einfach nicht klar daß die pauschale Aussage "Service Locator sind böse" sich nicht auf den grundsätzlichen Gebrauch von Container.Resolve bezieht, sondern nur darauf, Container.Resolve direkt in allen Klassen zu benutzen anstatt die Abhängigkeiten über den Konstruktor zu übergeben.
Eine Frage habe ich in dem Zusammenhang aber trotzdem noch:
Zitat von Peter Bucher
Bei der impliziten Anwendung geht es darum, das du an einem Ort ein container.Resolve<IFoo>() drin hast. Das kannst du dir als Resolution-Root vorstellen, also die Wurzel, wo die Auflösung beginnt.
Gibt es im Normalfall immer nur EINEN Resolution Root, oder viele?
Peter hat als Beispiel den Controller einer ASP.NET MVC-Anwendung genannt, aber mit ASP.NET MVC kenne ich mich nicht wirklich aus. Kommt man da wirklich mit einer einzigen Stelle in der ganzen Anwendung aus wo Container.Resolve benutzt wird?
Meine ersten Gehversuche mit DI/IoC mache ich gerade in Winforms-Anwendungen, und wenn ich da z.B. in mehreren verschiedenen Forms einen AuftragService brauche, wie komme ich an den?
In jedem Form Container.Resolve?
Für die Forms Interfaces schreiben, IAuftragService als Konstruktorparameter übergeben und das Winform an sich per Resolve erzeugen? (aber dann habe ich auch wieder an jeder Stelle wo ich ein Form lade Container.Resolve stehen)
Oder schreibe ich eine AuftragServiceFactory die intern Container.Resolve<IAuftragService> ausführt und benutze die überall?
Oder ist es gar nicht so schlimm wenn ich an X Stellen in der Anwendung direkt Container.Resolve aufrufe, solange es nur in der Infrastruktur und UI passiert und nicht in den eigentlichen Domain-Objekten?
wenn du dir mein obiges Bsp. anschaust - speziell iocContainer.Resolve<ICalculator>(); - dann ist das ein Resolution-Root.
Gibt es jetzt noch zB
public interface ILogger { }
public class MyLogger : ILogger { }
public interface IFooService { }
public class MyFooService : IFooService
{
private readonly ILogger _logger;
public MyFooService(ILogger logger) { ... }
}
dann muss der IFooService auch aufgelöst werden und dieser ist dann ein anderer Resolution-Root, da dieser der Ausgang für die Erzeugung von MyFooService ist und implizit wird ILogger aufgelöst.
Resolution-Root hat also nichts mit dem zu tun wie oft dieser im Code steht, sondern damit was als Wurzel für die implizite Auflösung verwendet wird. Es kann davon also durchaus mehrer geben. Die Resolution-Roots müssen extern aufgelöst werden.
Geh die letzten Beiträge nochmal durch dann sollten sich deine anderen Fragen klären.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Vielfach reicht ein Resolution-Root, und ggf. ein paar Service Locator-Aufrufe.
Bei Windows Forms könntest du die Forms über container.Resolve<IMainForm>();, etc... auflösen.
Oder direkt über container.Resolve<MainForm>();, mit http://lightcore.ch/ ist es möglich, konkrete Klassen vom Container zu nutzen, das die Abhängigkeiten über den Konstruktor bekommen.
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
Ich fürchte, ich habe es immer noch nicht verstanden.
Wenn ich die Forms mit container.Resolve<Mainform>() öffne, dann brauche ich doch an JEDER Stelle, wo ich ein Form öffne, einen Service-Locator-Aufruf.
Peter schreibt, es können "ggf. ein paar Service Locator-Aufrufe" sein.
Bei dem Beispiel mit den WinForms klingt mir das in einer größeren Anwendung aber eher nach hunderten Service Locator-Aufrufen.
Und die sind auch noch kreuz und quer über die ganze Anwendung verteilt (vom Startform öffne ich ein Form mit einer Auftragsliste, von dort aus ein neues mit Details zu einem bestimmten Auftrag, von dort ein neues mit Details zu einer einzelnen Position...)
Jetzt könnte ich natürlich den Container irgendwie kapseln damit ich nur noch ein einer einzigen Stelle wirklich direkt den Container benutze.
Aber ob jetzt an 100 Stellen im Code container.Resolve<IForm>() steht, oder GlobaleWinFormFactory.Create<IForm>(), das macht auch keinen großen Unterschied.
Ich habe dadurch eigentlich nur eine fest verdrahtete Abhängigkeit durch eine andere ersetzt.
Aber du brauchst doch den Servicelocator nur dort, wo du erst zur Laufzeit weißt welche oder wieviele Objekte du brauchst.
Wenn ich schon im vorhinein weiß, dass mein "MainForm" ein "PopupForm" hat, dann lass ich das gleich im Konstruktor injecten und verwende nur diese eine Instanz weiter. Ich brauche also hier keinen ServiceLocator im MainForm.
Wenn ich aber eine beliebige Anzahl an Fenstern aus meiner "MainForm" aus öffnen kann, brauche ich eine Art Factory, die mir die Fenster erzeugt und diese wird wiederum den ServiceLocator behinhalten. Aber außer in Factories wirst du nirgends den ServiceLocator direkt brauchen.
Und gegen einer solchen Verwendung spricht mMn. nichts - man trennt die Zuständigkeiten und bleibt ausgezeichnet testbar.
Ich habe dadurch eigentlich nur eine fest verdrahtete Abhängigkeit durch eine andere ersetzt.
Wenn du nicht mit Interfaces arbeitest, dann ist das tatsächlich so.
Solltest du das mit den Interfaces und der Trennung dann aber irgendwann mal richtig verstanden haben, und verstanden haben das auch die Factory per DI Injected werden sollte, dann bestehen keine festen Abhängigkeiten, auch nicht zum SL.
Jetzt wo Du es geschrieben hast leuchtet es mir ein und ich verstehe es.
Es ist auch nicht so schwer zu verstehen...es muß einem einfach nur mal einer sagen.
Also:
public class WinFormFactory : IWinFormFactory
{
private readonly IContainer container;
public WinFormFactory(IContainer container)
{
this.container = container;
}
public T GetForm<T>()
{
return (T)container.Resolve<T>();
}
}
public partial class MainForm : Form
{
private readonly IWinFormFactory factory;
public MainForm(IWinFormFactory factory)
{
this.factory = factory;
InitializeComponent();
}
}
static class Program
{
static void Main()
{
var container = new Container();
container.Register<MainForm>();
Application.Run(container.Resolve<MainForm>());
}
Dann kann ich hinterher irgendwo im MainForm folgendes machen:
private void Button1_Click(object sender, EventArgs e)
{
var form = this.factory.GetForm<PopupForm>();
form.Show();
}
So habt ihr das gemeint, oder?
Zusätzliche Frage:
Ich habe in dem Code oben jetzt keine Interfaces für die WinForms benutzt, sondern benutze direkt die Forms selber.
Macht es Sinn für die Forms auch Interfaces zu benutzen? Bei anderen Sachen (Repositories usw.) ist der Sinn klar, aber bei Forms habe ich es weggelassen weil ich den Sinn nicht sehe.
Ich möchte ja nicht zur Laufzeit das MainForm austauschen können o.ä., sondern ich benutze den Container nur damit das Form automatisch seine Abhängigkeiten bekommt.
und die instanzierte Klasse bekommt vom IoC den fehlenden Parameter injected
Soweit habe ich es verstanden und ich glaube damit sind alle Fälle abgedeckt die man so im täglichen Leben braucht.
Und nun die Frage, für was brauche ich dann einen DI-Container?
Ich lese immer wieder von ihm, aber nirgends finde ich eine Erklärung wie das Ding aussieht oder was es eigentlich tut.
Habe ich mich irgendwo in der Begriffeflut verirrt oder verstehe ich nicht was es ist?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von spike24 am .
mbg
Rossegger Robert
mehr fragen mehr wissen
Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
Di-Container ist das Object das man braucht um IoC zu "erhalten".
Also sowas ähnliches wie mein Objekt mit dem Namen IoCServiceLocator.
Aber so wie Du es geschrieben hast, gibt es zwischen dem DI-Container und dem IoCServiceLocator einen Unterschied?
Kannst Du da eventuell noch einen Satz dazu loswerden?
mbg
Rossegger Robert
mehr fragen mehr wissen
Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen