Laden...

Dependency Injection mit Unity

Erstellt von Wax vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.743 Views
Wax Themenstarter:in
731 Beiträge seit 2006
vor 8 Jahren
Dependency Injection mit Unity

Hallo zusammen,

ich versuche mich zur Zeit in das Thema Dependency Injection einzuarbeiten und nutze dazu Ninject.

Ich habe mehrere Anwendungen, die mit demselben Datenmodell arbeiten für sich gesehen aber spezielle Anforderungen haben.

Nun wollte ich die Validierung des Models (mittels FluentValidation auslagern und in eine eigene Assembly packen. Der geeignete Validator für ein Modell soll per DI bereitgestellt werden. Deswegen dachte ich mir, dass man die Registrierung dieser Typen innerhalb der Validation-Assembly vornehmen sollte.

Das Model wird unter anderem von einer ASP.NET WebApi 2 Anwendung verwendet. Diese hat auch Typen, welche per DI bereitgestellt werden sollen. Also bekommt die Anwendung auch eine "DI-Konfiguration".

Dies ist die Stelle, an der ich nicht mehr weiter weiß. Ist dieser Ansatz überhaupt gut gewählt und wenn ja, wie baut man das mit z.B. Ninject nun auf?
Teilen sich alle Komponenten ein und dieselbe Ninject-Kernel-Instanz oder gibt es mehrere?
Muss man dediziert einen Kernel erzeugen und diesen dann durch die Komponenten zur Registrierung ihrer Typen durchreichen?

Ich hoffe ihr könnt etwas Licht ins Dunkel bringen und mir weiterhelfen.

Gruß,
wax

edit: Titel auf Unity geändert

16.834 Beiträge seit 2008
vor 8 Jahren

Ich empfehl Dir auf NInject zu verzichten und auf Unity zu setzen.
In der nächsten Version von ASP.NET (Core 1) wird nämlich Unity (bzw. DI-Container, der Unity sehr sehr sehr ähnlich ist) direkt mitgeliefert.

Bin mir nicht sicher, ob ich Dich richtig verstanden hab; aber ich setze dies so um, dass meine Business-Schicht ebenfalls einen Resolver hat, den ich dann Unity in ASP.NET bekannt mache.
So kann ich Di ohne Aufwand in allen Anwendungstypen (ASP.NET, Console...) nutzen.

Wax Themenstarter:in
731 Beiträge seit 2006
vor 8 Jahren

Hi Abt,

ich habe mich nun mit Unity auseinandergesetzt.
Mein Ansatz ist zur Zeit der, dass ich den IUnityContainer zur Registrierung der Typen durch die Projekte reiche. Dazu benötigen ich von meiner Composition Root aus aber immer Referenzen auf die Implementierungs-Prjojekte, da nur diese Interface UND Implementierung kennen.

Da muss doch was falsch sein. Ich dachte man würde nur an den Interfaces hängen!? So ist mein "Main"-Projekt ja weiterhin von den Impl-Projekten abhängig.

Wie macht man sowas?

Gruß,
wax

2.207 Beiträge seit 2011
vor 8 Jahren

Hallo Wax,

ich bin mir nicht sicher, ob ich dich richtig verstanden habe. Aber ich hatte auch mal die Frage hier im Forum gestellt, dass bei der Registrierung meine Klassen mein Root auch immer auf die *.Impl-Projekte verweisen muss, da man sie ja sonst nicht registrieren kann. Ich finde den Thread aber gerade nicht mehr...

Jedenfalls war die Antwort, dass man vom Root aus einfach damit lebt, dass eine Referenz auf das Impl-Projekt ist und es nur dort verwendet, wo es in den DI Container geschmissen wird. Andere Verweise sollten dann immer über das Interface/API-Projekt laufen.

Ich suche den Thread mal weiter...

Gruss

Coffeebean

F
10.010 Beiträge seit 2004
vor 8 Jahren

Naja, i.a macht man bei seinen Plugin fähigen Projekten ja ein Interface fürs ModulInit, das dann das Registrieren der Objekte/Klassen im übergebenen ( I ) Container übernimmt.
So muss nur das Modul die konkrete Implementation kennen.

16.834 Beiträge seit 2008
vor 8 Jahren

Ich halte nichts von Impl-Projekten - wir sind ja nicht bei Java. 😃
Dependency Injection ist nicht dazu da, dass man die vollkommene Unabhängigkeit bekommt und die Referenzen nicht kennen muss.
Spätestens beim Zugriff auf den jeweiligen Typ muss man die Referenz haben - oder im Falle von DI das entsprechende Interface.

Bei mir erzeugt in den Projekten i.d.R. die Middleware / Engine den Unity-Container.


using BenjaminAbt.TestApp.Engine.Factories;
using BenjaminAbt.TestApp.Engine.Registrators;

namespace BenjaminAbt.TestApp.Engine

private EngineBoostraper()
{
    _container = new UnityContainer();

    _container.RegisterType<IQueryFactory, AssemblyCachedQueryHandlerFactory>();
    _container.RegisterType<ICommandFactory, AssemblyCachedCommandHandlerFactory>();

    CommandRegistrator.Register( _container );
    QueryRegistrator.Register( _container );
}

Die Engine bzw. das Registering hat dann die Referenzen, zumindest auf die Interfaces bzw. beim Registrieren auch auf die Typen.


namespace BenjaminAbt.TestApp.Engine

public static void Register( IUnityContainer unityContainer )
{
    unityContainer.RegisterType<IPersonsQuery, PersonsQuery>( nameof( PersonsQuery ) );
    unityContainer.RegisterType<IPersonByIdQuery, PersonByIdQuery>( nameof( PersonByIdQuery ) );
}

Die Applikation (hier als Beispiel WebAPI), die aber den Unity-Container hält, muss die Implementierungen nicht kennen!


namespace BenjaminAbt.TestApp.WebApp

public static void Register( HttpConfiguration config )
{
    var container = EngineBoostraper.Container;

    config.DependencyResolver = new UnityResolver( container );

namespace BenjaminAbt.TestApp.WebApp.Persons

public class PersonsController : ApiController
{
    private readonly IQueryFactory _queryFactory;

    public PersonsController( IQueryFactory queryFactory )
    {
        _queryFactory = queryFactory;
    }

Wenn jetzt der Controller nicht im Hauptprojekt sondern in einem Modul ist, dann kennt das WebAPI Projekt keine einzige Referenz ausser zum EngineBootstrapper.
Die Plugins selbst können ja über viele Wege in die AppDomain kommen (oder man gibt ihnen den Container).
Und das ist der riesige Vorteil (oder ich hab Deine Frage nicht verstanden).

Wax Themenstarter:in
731 Beiträge seit 2006
vor 8 Jahren

@Abt: Könntest du bitte einmal deine Aussage "Ich halte nichts von Impl-Projekten - wir sind ja nicht bei Java. 😃" begründen. Würde mich interessieren.

Gruß,
wax

16.834 Beiträge seit 2008
vor 8 Jahren

Keine Ahnung, worauf Du nun hinaus willst...
Aber in Java ist es ja oft so, dass diese auf nen Präfix beim Interface verzichten, dafür die Implementierung eines Interfaces mit Impl dekorieren


public class Socket : ISocket


public class SocketImpl 
    implements Socket {

Und ich halte nichts davon (C#), wenn man in Projekt MyName.MyProject.Socket.Interfaces seine Interfaces deklariert und MyName.MyProject.Socket.Impl für die Implementierung nutzt.
Weiß jetzt nicht, ob das jetzt Coffeebean so macht (hoffentlich nicht 😃 ) aber ich hab sowas schon oft gesehen.

2.207 Beiträge seit 2011
vor 8 Jahren

Weiß jetzt nicht, ob das jetzt Coffeebean so macht (hoffentlich nicht 🙂 ) aber ich hab sowas schon oft gesehen.

Ich wusste, dass sowas kommt. 😉 Hab nur drauf gewartet.

Hat alles Vor- und Nachteile. Da ich bei vielen verschiedenen Kunden im Einsatz bin und war habe ich das auch schon oft gesehen. Ich sage das ohne Wertung an der Stelle Aber es stimmt: Oft haben die Impl-Leute Java-Vergangenheit. ("Impl" bezüglich Klassennamen, Namespaces oder sogar auf Projekt-Ebene)

Aber die Diskussion ob das gut oder schlecht ist lassen wir an der Stelle bleiben!

Gruss

Coffeebean