2012-04-15 19 views
2

Ich bin auf der Suche nach einem IoC-Container wie StructureMap in einer neuen Lösung. Die Projekte Lösung sind wie folgt:Dependency Injection - Layered Application

  • Desktop.UI
  • Desktop.Models
  • Desktop.Views
  • Desktop.Presenters
  • Businesslogic
  • Business
  • Data Access

Wo Desktop.Models, BusinessLogic und DataAccess alle einen Verweis auf BusinessObjects haben.

Desktop.UI-Projekt wäre der Einstiegspunkt, wo DI konfiguriert werden sollte, aber ich möchte nicht wirklich einen Verweis auf DataAccess nur für DI hinzufügen.

Ich habe ein paar andere Fragen in dieser Richtung auf SO gesehen und eine Antwort war, ein separates Projekt für IoC zu erstellen, das auf die relevanten Projekte verweist und dann IoC-Projekt von Desktop.UI referenziert. Desktop.UI benötigt jedoch eine DI-Konfiguration, um Präsentatoren für Ansichten zu instanziieren, so dass ein separates Projekt nicht funktioniert.

Ich habe einige grundlegende Beispiele für die Definition von Registry-Klassen und die Verwendung der Scanning-Funktion StructureMap zur automatischen Erkennung der Registry-Klassen gesehen. Mein erster Gedanke war, diese Registry-Klassen in die Projekte zu stellen, für die sie konfiguriert sind, was jedoch dazu führt, dass alle Projekte auf StructureMap verweisen müssen.

Was ist der empfohlene Ansatz zum Einrichten von DI in einer mehrschichtigen Anwendung?

+0

Wenn ich Sie wäre ich ein Rahmenprojekt schaffen würde ein Singleton IoC zu speichern und Ihre Bootstrapping-Code drin. –

+0

@jameslewis Wie würde das mit dem UI-Projekt funktionieren, da das UI-Projekt DI für Präsentatoren benötigt, also wenn das IoC in einem separaten Projekt war, das die UI referenziert, konnte das IoC-Projekt nicht auch auf UI verweisen? –

Antwort

1

Nach meiner Erfahrung habe ich festgestellt, dass ich eine Art der Abhängigkeitsinjektion in meinem serverseitigen Code und einen in meinem clientseitigen Code verwendet habe. Zum Beispiel arbeitete das WinForms-Projekt, an dem ich gerade arbeitete, auf der Serverseite (Geschäftslogik, Datenzugriff) und Smart-Client-Software-Factory auf der Client-Seite (MVP-Framework für Winforms, die eine eigene Form von DI enthält)). In Ihrem Beispiel würde ich sagen, wenn Sie Schwierigkeiten hätten, ein DI-Framework für alle Ebenen Ihrer Anwendung zu verwenden, dann liegt das daran, dass Sie es nicht sein sollten. Sie haben wahrscheinlich einen Fall, in dem Sie zwei Arten von DI haben möchten, eine für den Client und eine für den Server.

Außerdem denke ich, dass die meisten DI-Frameworks es Ihnen ermöglichen, Ihre Abhängigkeiten in einer Konfigurationsdatei zu laden. Die Konfigurationsdatei teilt dem Framework alles über Ihre Abhängigkeiten mit, einschließlich der Assemblies, in denen sie sich befinden. Dies bedeutet, dass Sie nicht auf Ihre tatsächlichen Projekte in dem Projekt verweisen müssen, in dem Sie Ihren IoC-Container instanziiert haben. In meinem Beispiel eines Framework-Projekts haben Sie also ein Singleton, das Ihren IoC-Container instanziiert und alle Abhängigkeiten aus einer Konfigurationsdatei gelesen hat.

Hier einige Links für Sie:

Hoffnung einige das hilft!

Grüße,

James

+0

Danke für die Antwort. Meine Desktop-Client-, Business-Logik- und Datenzugriffs-Projekte werden alle zusammen in dieser Lösung bereitgestellt, so dass es immer noch möglich ist, sie separat mit der fließenden Syntax anstelle von Konfigurationsdateien zu konfigurieren, oder Konfigurationsdateien wären der Weg, wenn ich nicht möchte Verweis Datenzugriff von UI? –

+0

Ich würde mit Konfigurationsdateien gehen. Persönlich haben wir einen "intelligenteren" Bootstrapper geschrieben, der Reflektion verwendet, um Abhängigkeiten aufzulösen, so dass ich die config-Methode eine Weile nicht benutzt habe (und wir hatten auch kein Problem mit der Referenzierung von Assemblies). Aber ich würde sagen, alles zu entkoppeln ist, wofür die Konfigurationsoption da ist, also klingt es wie in deinem Fall, das ist der Weg zu gehen. –

+0

@MattF Ich würde definitiv keine Konfigurationsdateien verwenden. Der Wartungsaufwand ist zu groß. Außerdem ist es fehleranfälliger. http://martinfowler.com/articles/injection.html#CodeOrConfigurationFiles Werfen Sie einen Blick auf meine Antwort. – Rookian

1

Für Windows habe ich ein eigenes Projekt für meine IoC Sachen. Ich nenne es CompositionRoot.

Einstieg von CompositionRoot (Startprojekt):

public static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 

     BootsTrapper.Boot(); 

     using (var mainForm = ObjectFactory.GetInstance<IPresenter<IMainView>>()) 
     { 
      Application.Run((Form)mainForm.CurrentView); 
     } 
    } 
} 

Die BootsTrapper ist verantwortlich für die die Registrierung als und ist ein Teil der CompositionRoot.

Der MainViewPresenter zeigt die erste Form an. MainViewPresenter kann andere Formulare anzeigen, indem der Fassadenservice IMainViewPresenterFacade verwendet wird, der abstrakte Präsentationsfabriken enthält.

public class MainViewPresenter : Presenter<IMainView> 
{ 
    readonly IArticleRepository _articlesRepository; 
    readonly IMainViewPresenterFacade _presenterFactory; 
    readonly IUnitOfWork _unitOfWork; 

    public MainViewPresenter(IMainView currentView, IArticleRepository articlesRepository, IUnitOfWork unitOfWork, 
          IMainViewPresenterFacade presenterFactory) 
     : base(currentView, unitOfWork) 
    { 
     _articlesRepository = articlesRepository; 
     _unitOfWork = unitOfWork; 
     _presenterFactory = presenterFactory; 

     Ensure.That(articlesRepository).IsNotNull(); 
     Ensure.That(presenterFactory).IsNotNull(); 

     CurrentView.DetailsClick += View_DetailsClick; 
     CurrentView.CloseClick += ViewCloseClick; 
     CurrentView.CreateClick += View_CreateClick; 
     CurrentView.DeleteClick += View_DeleteClick; 

     CurrentView.BindModel(_articlesRepository.GetAll().Select(x => new ArticleViewModel { Id = x.ArticleId, Name = x.Description })); 
    } 

    public override void Dispose() 
    { 
     base.Dispose(); 

     CurrentView.DetailsClick -= View_DetailsClick; 
     CurrentView.CloseClick -= ViewCloseClick; 
     CurrentView.CreateClick -= View_CreateClick; 
     CurrentView.DeleteClick -= View_DeleteClick; 
    } 

    void View_DeleteClick(object sender, EventArgs e) 
    { 
     var selectedArticle = CurrentView.GetSelectedArticle(); 
     var article = _articlesRepository.GetById(selectedArticle.Id); 
     _articlesRepository.Delete(article); 
     _unitOfWork.Commit(); 
    } 

    void View_CreateClick(object sender, EventArgs e) 
    { 
     using (var createPresenter = _presenterFactory.CreateCreatePresenter()) 
     { 
      ShowDialog(createPresenter.CurrentView, CurrentView); 
     } 
    } 

    void ViewCloseClick(object sender, EventArgs e) 
    { 
     CurrentView.Close(); 
    } 

    void View_DetailsClick(object sender, EventArgs eventArgs) 
    { 
     var article = CurrentView.GetSelectedArticle(); 

     if (article == null) return; 

     using (var detailPresenter = _presenterFactory.CreateDetailPresenter(article)) 
     { 
      ShowDialog(detailPresenter.CurrentView, CurrentView); 
     } 
    } 
} 

Werfen Sie einen Blick auf meine SimpleMVP Projekt oder auf diese Frage Dependency Injection and project structure for Console applications und fühlen sich frei, weitere Fragen zu stellen :)