Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
DI/IoC in der Praxis (und korrekter Aufbau der zugehörigen Klassen)
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

@haarrrgh:
Doch genau Resolve ist es.

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.
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 5.940
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo haarrrgh

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.
(Expliziter Resolve Aufruf: Resolve<MeinController>();)
IController
 - ICustomRepository (implizit)
   - ILogger (implizit)
 - ILogger (implizit)
 - ISessionService (implizit)

...

Bei expliziter Anwendung würde das CustomerRepository explizit einen Logger erstellen, usw.

Klarer?


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
private Nachricht | Beiträge des Benutzers
haarrrgh
myCSharp.de - Member



Dabei seit:
Beiträge: 208
Herkunft: Raum Köln

Themenstarter:

beantworten | zitieren | melden

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?
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.815
Herkunft: Waidring

beantworten | zitieren | melden

Hallo,

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!"
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 5.940
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo haarrrgh

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
private Nachricht | Beiträge des Benutzers
haarrrgh
myCSharp.de - Member



Dabei seit:
Beiträge: 208
Herkunft: Raum Köln

Themenstarter:

beantworten | zitieren | melden

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.

Das kann doch nicht der ganze Trick sein, oder?
private Nachricht | Beiträge des Benutzers
xxxprod
myCSharp.de - Experte

Avatar #avatar-2329.gif


Dabei seit:
Beiträge: 1.378
Herkunft: Österreich\Wien

beantworten | zitieren | melden

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.

Lg XXX
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

Zitat
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.
private Nachricht | Beiträge des Benutzers
haarrrgh
myCSharp.de - Member



Dabei seit:
Beiträge: 208
Herkunft: Raum Köln

Themenstarter:

beantworten | zitieren | melden

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.

Also nicht:

container.Register<IMainForm, MainForm>();
container.Resolve<IMainForm>();

sondern:

container.Register<MainForm>();
container.Resolve<MainForm>();

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.
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

Ja, es macht sinn, sobald du auch mal das mit den Unittests machst.

Denn dann kannst Du die Logik deiner SW getrennt von der UI testen, denn du hast ja ganz sicher die UI von der Logik getrennt ( MVC/MVP/MVVM/MVVMP ).

Aber das mit den Interfaces steht eigentlich bei jeder Einleitung zu IOC.
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

Grüss euch,

ich habe mir nun den ganzen Thread und alle verlinkten Seiten zur Gänze durchgelesen wie auch generell im Internet gestöbert.

Fazit:
IoC:

<component 
  id="internalname" 
  service="oRRoSoft.InterfaceWhichIsImplemented, oRRoSoft" 
  type="oRRoSoft.TypeWhichShouldBeResolved, oRRoSoft" />
ergibt:

IoCServiceLocator.Resolve<InterfaceWhichIsImplemented>();

DI besteht im eigentlichen Sinne nur aus einem ctor:

public DITestClass(IExternalDependency externalDependency) { /* code */ }

woher die Klasse die Instanze bekommt ist dem DI Prinzip egal.

Die Kombination draus wäre:
<component 
  id="dependecy" 
  service="oRRoSoft.IExternalDependency, oRRoSoft" 
  type="oRRoSoft.ExternalDependency, oRRoSoft" />
<component 
  id="internalname" 
  service="oRRoSoft.InterfaceWhichIsImplemented, oRRoSoft" 
  type="oRRoSoft.TypeWhichShouldBeResolved, oRRoSoft">
  <parameters>
    <externalDependency>${dependency}<externalDependency/>
  </parameters>
</component>
ergibt:

IoCServiceLocator.Resolve<InterfaceWhichIsImplemented>();
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
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

IOC == Inversion Of Control
Das ist der Name eines Pattern

DI-Container
Das ist eine Klasse die die Auflösung von Abhängigkeiten gemäß IOC erledigt.
Hier werden ggf ganze Trees erzeugt.

ServiceLocator
Eine weitere Klasse die es ermöglicht ein einzelnes Objekt gemäß IOC vorgaben zu erstellen.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von FZelle am .
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

aahhh ...

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
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo spike24,
Zitat
Aber so wie Du es geschrieben hast, gibt es zwischen dem DI-Container und dem IoCServiceLocator einen Unterschied?
alles schon ausführlich besprochen:

Service Locator und Dependency Injection Container: Was sind die Unterschiede? Wann was verwenden?

herbivore
private Nachricht | Beiträge des Benutzers
spike24
myCSharp.de - Member



Dabei seit:
Beiträge: 443
Herkunft: Steiermark Österreich

beantworten | zitieren | melden

Ist mir da ein Beitrag ausgekommen, herzlichen Dank für den Link.
Denke jetzt habe ich es kapiert und kann es einsetzen.

:top:
mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen
private Nachricht | Beiträge des Benutzers