Letzte Woche habe ich einige Artikel über die Verwendung von WCF, den Entity -Framework und den Transport von Unternehmen über die Servicegrenze gelesen. Einer der Artikel, auf die ich gestoßen bin, enthielt ein Demo -Projekt, das das MVP -Muster (Model View Moderator) verwendet hat.
Nachdem ich das Demo -Projekt untersucht hatte, dachte ich, es könnte interessant sein, einen Artikel über dieses Muster zu schreiben. Ihre bevorzugte Suchmaschine liefert Ihnen gerne eine Vielzahl von Links zu anderen Artikeln, die dieses Muster gründlich erklären.
Für diesen Artikel habe ich beschlossen, ein konkretes Implemention zu liefern und sich weniger auf die Theorie hinter dem Muster zu konzentrieren.
Lass uns rollen ...
Natürlich ist ein bisschen Theorie erforderlich, also lass es uns aus dem Weg räumen.
Wie Sie vom Namen abziehen können, besteht das MVP -Muster aus drei verschiedenen Teilen: dem Modell, der Ansicht und dem Moderator. Jede dieser Teile spielt ihre eigene Rolle bei der Festlegung einer Trennung von Bedenken zwischen der Präsentation, der Geschäfts- und Datenzugriffsschicht.
Das Modell ist für den Umgang mit dem Datenzugriff verantwortlich. Der Moderator kommuniziert mit dem Modell und übergibt Daten von und an. Die Ansicht empfängt Daten vom Moderator und übergibt Daten zurück. Sie kommuniziert nie direkt mit dem Modell. Der Moderator ist der Auftritt für die Aussicht und das Modell.
Abbildung 1 - MVP -Muster -Wechselwirkung

Das obige Bild zeigt die Ansicht als Implementierung einer Schnittstelle. Die Präsentationsebene sei es ein ASP.NET-, WinForms- oder WPF -Anwendungen, um eine oder mehrere Ansichtsschnittstellen zu implementieren. Der Moderator wiederum kommuniziert mit der Implementierung der Ansicht über diese Schnittstelle nichts über die tatsächliche Implementierung selbst.
Dies liefert eine lockere Kopplung und verhindert, dass Ihr Code von der für die Präsentationsschicht verwendeten Technologie abhängig ist. Die Idee ist, dass Sie in der Lage sein sollten, diese Ebene zu ersetzen, ohne dass sie Ihre Unternehmens- und Datenzugriffslogik beeinflusst.
Dies mag alles ein wenig vage erscheinen, aber die Dinge sollten aufklären, sobald wir zu den folgenden Punkten übergehen, die eine tatsächliche Implementierung dieses Musters liefern. Zum Beispiel wird eine ASP.NET -Anwendung verwendet.
Der logischste Ort ist das Modell. Da es für den Umgang mit dem Zugriff und Speicher von Daten verantwortlich ist, müssen wir zunächst einen physischen Datenspeicher einrichten. Zu diesem Zweck habe ich SQL Server 2005 Express verwendet, eine neue Datenbank namens Southwind erstellt und eine Tabelle mit dem Titel Customer hinzugefügt. Die Tabelle hat 3 Felder, nämlich:
Das ist es für die Datenbank. Lassen Sie uns Visual Studio 2008 aufstellen und eine neue leere Lösung namens Avppattern erstellen. Fügen Sie als nächstes eine Klassenbibliothek mit dem Titel "Datenbank" hinzu.
Normalerweise würde ich dem Projekt einen Namen geben, der dem Muster "Company.Product.library" folgt, aber zum Einfachheit halber halten wir es kurz und einfach. Löschen Sie auch die autogenerierte Class1.cs -Datei, nachdem das Projekt zur Lösung hinzugefügt wurde.
Erstellen wir ein Modell aus der Datenbank mit dem Entity Framework (EF). Stellen Sie also sicher, dass Sie Visual Studio 2008 verwenden und Service Pack 1 für Visual Studio und das .NET Framework 3.5 installiert haben. Sie können die Service Packs hier herunterladen.
Fügen Sie der Klassenbibliothek ein Entity -Framework -Datenmodell hinzu, indem Sie hinzufügen, neues Element, ADO.NET -Entitätsdatenmodell aus dem Kontextmenü des Projekts im Lösungsexplorer auswählen. Visual Studio zeigt nun den Assistenten des Entity -Datenmodells an. Nennen Sie das Modell Southwind und lassen Sie Visual Studio das Modell für Sie generieren. Auf die Frage, welche Datenbankobjekte Sie in Ihr Modell aufnehmen möchten, wählen Sie einfach die Kundentabelle aus dem Tabellenknoten aus.
Bemerkung : Wenn Sie nicht mit dem Generieren von Datenmodellen mit dem Entity -Framework vertraut sind, empfehle ich dieses Trainingsvideo von Alex James sehr. Es zeigt Ihnen, wie Sie ein einfaches Entitätsdatenmodell von Grund auf neu erstellen.
Abbildung 2 zeigt das resultierende Entitätsdatenmodell. Das Modell kann nicht viel einfacher werden. Dies geschieht absichtlich, um die Dinge so einfach wie möglich zu halten und sich auf das MVP -Muster zu konzentrieren.
Abbildung 2 - Entitätsdatenmodell

Nennen Sie Ihre Entitäts- und Entitätssätze nach dem Erstellen des Datenmodells unbedingt in einen geeigneten Namen um. Die Faustregel lautet, ein einzelnes Substantiv für den Entitätstyp und einen Plural für die Entitätssätze zu verwenden. In diesem Fall sollte unser EntityType als Kunde und die Unternehmen für Unternehmen als Kunden ernannt werden. Der Name für den EntityType ist bereits in Ordnung, da er ihn aus der Kundentabelle erbt. Wählen Sie also einfach die Kundenentität -Type aus und passen Sie seine Entitätsname -Eigenschaft an.
Abbildung 3 - Entitätsname

Das Entity Framework generiert automatisch eine Teilklasse für die Kundentabelle. Sie können diese partische Kundenklasse erweitern, wenn Sie möchten. Um zu verhindern, dass Ihre benutzerdefinierten Ergänzungen beim Regenerieren des Modells gelöscht werden, legen Sie diesen Code in eine separate Klassendatei ein. Dies ist gleichbedeutend mit der Arbeit mit stark getippten Datensätzen. Für den Artikel ist dies jedoch nicht erforderlich.
Abbildung 4 - Lösungsforscher

Jetzt, da das Modell vorhanden ist, fügen wir eine Geschäftsschicht darüber hinzu, in der wir unsere benutzerdefinierte Geschäftslogik definieren können. Der Einfachheit halber werde ich die Menge an Code in dieser Ebene begrenzt halten.
Beachten Sie, dass diese Schichten nur eine logische Trennung (N-Schicht) erzwingen, keine physische (n-tier). Die Schichten befinden sich alle auf derselben Maschine, obwohl Sie sich sicherlich dafür entscheiden könnten, sie über mehrere Maschinen / Ebenen hinweg zu veranlassen und eine wirklich verteilte oder n-Tier-Anwendung zu erstellen.
Um die Business Layer einzurichten, fügen Sie der Lösung eine neue Klassenbibliothek hinzu und nennen Sie sie Business. Benennen Sie als nächstes die Standardklasse.CS -Datei in CustomerManager.cs um. Fügen Sie auch Verweise auf die zuvor erstellte Klassenbibliotheksdatenbank und zum System hinzu.Data.Entity Assembly.
Auflistung 1 zeigt die CustomerManager -Klasse an, die eine Geschäftslogik für die Zusammenarbeit mit der Kundenentität aus dem Entity Data Model (EDM) enthält. Der Code ist so ziemlich selbsterklärend.
Listing 1 - CustomerManager -Klasse
using System . Collections . Generic ;
using System . Linq ;
using Database ;
namespace Business
{
public class CustomerManager
{
private readonly SouthwindEntities context ;
#region Constructor(s)
public CustomerManager ( )
{
context = new SouthwindEntities ( ) ;
}
#endregion
#region Methods
// Retrieve a generic list of Customer entities.
// This method will return all the customers found in the Customer table.
public List < Customer > GetCustomers ( )
{
var q = from c in context . Customers
select c ;
return q . ToList ( ) ;
}
#endregion
}
}Abbildung 5 - Aktualisierte Lösung

Bemerkung : Das Einrichten einer N-Tier-Anwendung umfasst die Einführung einer Serviceschicht, in die die Präsentationsebene aufruft. Die Serviceschicht verwendet dann die in der Geschäftsschicht gefundenen Geschäftsobjekte. Es gibt keinen direkten Zusammenhang zwischen der Präsentation und der Geschäftsschicht mehr, die Serviceschicht fungiert als Zwischenprodukt. In einem zukünftigen Artikel werde ich dies ansprechen, indem ich zeigt, wie EF -Unternehmen über die Servicegrenze transportiert werden.
Die Geschäftsschicht enthält nur eine nützliche Methode, nämlich "Liste getCustomers ()". Der Moderator im MVP -Muster nennt diese Methode im CustomerManager -Geschäftsobjekt, um die Daten an die Ansicht zu liefern.
Die Beispielanwendung zeigt nur eine Liste von Kunden, die das MVP -Muster verwenden. Dies mag ein bisschen übertrieben sein, aber es ist so einfach wie möglich. Das Hauptziel dieser "Hallo Welt!" Die Art der Anwendung besteht darin, die Idee zu vermitteln, wie dieses Muster implementiert werden soll. Die tatsächliche Funktionalität der Anwendung ist nicht so wichtig.
Die tatsächliche Ansichtsimplementierung (ASPX -Seite, WinForms, WPF ... usw.) muss eine Ansichtsschnittstelle implementieren. Die Ansichtsimplementierung muss eine Instanz des Moderators erstellen und sich als Parameter in seinem Konstruktor übergeben. Der Konstruktor des Moderators hat einen Parameter, der die Art der Ansichtsschnittstelle ist.
Listing 2 listet die IView -Schnittstelle auf, die wir in Kürze auf einer ASP.NET -ASPX -Seite implementieren werden. Es hat eine Veranstaltung mit dem Namen Prepeview. Das PrepeView -Ereignis verwendet einen Delegierten, dessen Signatur angibt, dass es nichts zurückgibt und keine Parameter entgegennimmt.
Die Ansicht sollte diese Art von "leeren Ereignissen" nur erhöhen, um dem Moderator einige Maßnahmen auszuführen. Die Aktion in diesem Fall bedeutet, dass der Moderator die Liste der Kunden, die die Ansichtsimplementierung verwaltet, aktualisieren sollte. Der Moderator kann diese Liste der Kunden über die Ilist -Kunden -Immobilie zugreifen, die als Teil der Schnittstelle deklariert wird.
Listing 2 - Schnittstelle anzeigen
public delegate void VoidEventHandler ( ) ;
public interface IView
{
event VoidEventHandler PrepareView ;
IList < Customer > Customers { set ; }
}So fügen Sie Ihr Projekt die Ansichtsoberfläche hinzu, die zuerst ein neues Klassenbibliotheksprojekt zur Lösung mit dem Namen Präsentation hinzufügen. Fügen Sie als nächstes eine neue Schnittstelle hinzu und kopieren Sie den Code in der obigen Auflistung und fügen Sie sie ein und fügen Sie sie ein. Die Präsentationscode -Bibliothek enthält auch die Moderatoren und die von ihnen implementierten Schnittstellen.
Ich trenne die View -Schnittstellen und Präsentatoren in einer separaten Codebibliothek, damit Sie sie einfach zwischen mehreren "View -Frameworks" wie ASP.NET, WinForms, WPF ... usw. teilen können. Abbildung 6 zeigt, wie ich mich für die Organisation dieser Codebibliothek entschieden habe.
Abbildung 6 - Aktualisierte Lösung

Vergessen Sie nicht, Referenzen auf die Datenbank- und Geschäftsprojekte und das System hinzuzufügen.Data.Entity Assembly.
Für den letzten Teil des MVP -Musters müssen wir einen Moderator zur Verfügung stellen. Die in Listing 3 angezeigte Customer -Spresenter -Klasse nimmt einen Verweis auf eine IView -Implementierung in seinem Konstruktor. Auf diese Weise kann es mit der Ansicht kommunizieren, ohne etwas über die tatsächliche Implementierung zu wissen. Es ist diese lose Kupplung, die das MVP -Muster für verschiedene "Ansichts -Frameworks" so geeignet macht.
Auch im Konstruktor sind alle Ereignisse der Sichtschnittstelle an einen Event -Handler angeschlossen. In diesem Fall gibt es nur ein Ereignis. Das PrepeView -Ereignis ist an den Ereignishandler von View_prepareview angeschlossen. Dies senkt wiederum die private Methode des Moderators GetCustomers (), die eine "aktualisierte" Kundensammlung zurückgibt und der Kundensammlung zuweist, die durch die Ansichtsimplementierung beibehalten wird.
Listing 3 - CustomerPresenter -Klasse
public class CustomersPresenter : ICustomersPresenter
{
#region Fields
private readonly IView view ;
#endregion
#region Constructor(s)
public CustomersPresenter ( IView view )
{
// Save a reference to the view
this . view = view ;
// Hook up an event handler for the events of the view
view . PrepareView += view_PrepareView ;
}
#endregion
#region Private methods
private List < Customer > GetCustomers ( )
{
return new CustomerManager ( ) . GetCustomers ( ) ;
}
#endregion
#region ICustomersPresenter Members
public virtual void view_PrepareView ( )
{
view . Customers = GetCustomers ( ) ;
}
#endregion
}Der obige Präsentator implementiert auch eine Schnittstelle. Im Beispiel -Quellcode bleibt diese Schnittstelle leer. Ich habe es nur für veranschaulichende Zwecke dorthin gesetzt. Sie können diese Schnittstelle herausfinden, wenn Sie möchten. Möglicherweise benötigen Sie es für Ihren bevorzugten Unit -Test -Framework, um die Moderatoren zum Beispiel zu verspotten.
Daher muss die Ansicht oder ASPX -Seite in unserem Fall nur die Ansichtsschnittstelle implementieren und das PrepeView -Ereignis auslösen, um eine aktualisierte Kundenliste vom Moderator zu erhalten. Die Ansicht selbst kommuniziert nicht direkt mit der Datenbank oder der Geschäftsschicht. Der Moderator verarbeitet die Kommunikation mit der Geschäftsschicht, die die Daten abruft, indem es das Modell adressiert (oder die Datenzugriffsschicht, wenn Sie so).
Um diesen Artikel zu beenden, lassen Sie uns sehen, wie dies alles in einem ASP.NET -Demo -Projekt zusammenkommt. Fügen Sie der Lösung ein neues Projekt mit der ASP.NET -Webanwendungs -Projektvorlage hinzu. Fügen Sie Verweise auf die Präsentations- und Datenbankprojekte und das System hinzu.Data.Entity Assembly.
Fügen Sie der Seite "gridView1" und eine Schaltfläche "Btnrefresh" mit dem Namen "GridView1" und einer Schaltfläche "btnrefresh" hinzu. Fügen Sie den Code in Listing 4 zum Code hinter der Seite hinzu.
Listing 4 - Standard.aspx -Code dahinter
public partial class _Default : System . Web . UI . Page , IView
{
private CustomersPresenter presenter ;
protected override void OnInit ( EventArgs e )
{
presenter = new CustomersPresenter ( this ) ;
}
protected void Page_Load ( object sender , EventArgs e )
{
if ( ! IsPostBack )
{
PrepareView ( ) ;
}
}
protected void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public IList < Database . Customer > Customers
{
set
{
GridView1 . DataSource = value ;
GridView1 . DataBind ( ) ;
}
}
#endregion
}Der gesamte Code hinter der ASPX -Seite implementiert die IView -Schnittstelle, erstellen Sie einen Präsentanten und übergeben eine IView -Implementierung, die sich selbst selbst, in seinen Konstruktor. Dann bleibt alles, was noch übrig bleibt, das PrepeView () -Ergrad der IView -Schnittstelle zu den entsprechenden Zeiten auszulösen.
Während der Erstellung des Moderators wird dieses Ereignis automatisch zugewiesen, um sicherzustellen, dass der Moderator, wenn er ausgelöst wird, die von der Seite verwaltete Kundensammlung aktualisiert. Die Seite selbst weiß nichts, woher diese Daten stammen oder wie sie abgerufen wird. Der dumber ist eine Aussicht desto besser.
Beim Anzeigen dieser Seite in einem Browser ist dies das Ergebnis:
Abbildung 7 - ASP.NET -Website Demo

Bemerkung : Vergessen Sie nicht, die vom Entity Framework erforderliche ConnectionString zur Konfigurationsdatei von Web.config hinzuzufügen. Sie finden den ConnectionString in der Datei app.config des Datenbankklassenbibliotheksprojekts. Es wurde dort automatisch eingefügt, als Visual Studio das Entitätsdatenmodell generierte.
Natürlich funktioniert die Verbindungszeichenfolgen, die im Beispiel -Quellcode mitgeliefert wurden, auf Ihrem Computer nicht, da sie gegen eine lokale Datenbank von mir konstruiert wurden. Stellen Sie sie also entsprechend an.
Abbildung 8 - Aktualisierte Lösung

Als letzter Schritt dieses Artikels erstellen wir eine Ansicht mit einer Windows Forms -Anwendung, um Ihnen zu zeigen, wie flexibel das MVP -Muster wirklich ist. Die Schritte dazu sind nahezu identisch mit dem vorherigen Beispiel für das Erstellen einer ASP.NET -Website. Fügen Sie Ihrer Lösung eine neue Windows -Formulationsanwendung hinzu und fügen Sie der Datenbank- und Präsentationsprojekt und dem System.Data.Entity Assembly Verweise hinzu.
Fügen Sie als nächstes dem Formular eine DataGridView und Schaltfläche hinzu. Der Code für das Formular wird in der folgenden Auflistung angezeigt. Es ist fast identisch mit der der Seite Standard.aspx. Vergessen Sie auch nicht, die Konfigurationsdatei von App.Config die Konfigurationsdatei von App.Config hinzuzufügen.
Listing 5 - Form1.CS -Code
using System ;
using System . Windows . Forms ;
using Presentation . Presenters ;
using Presentation . ViewInterfaces ;
namespace WindowsFormsApplication
{
public partial class Form1 : Form , IView
{
private CustomersPresenter presenter ;
public Form1 ( )
{
InitializeComponent ( ) ;
presenter = new CustomersPresenter ( this ) ;
}
private void Form1_Load ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
private void btnRefresh_Click ( object sender , EventArgs e )
{
PrepareView ( ) ;
}
#region IView Members
public event VoidEventHandler PrepareView ;
public System . Collections . Generic . IList < Database . Customer > Customers
{
set
{
dataGridView1 . DataSource = value ;
}
}
#endregion
}
}Voila, wir sind endlich fertig. Beachten Sie, dass zwar das Beispiel ASP.NET und WinForms in Code nahezu identisch ist, dies in komplexeren Anwendungen möglicherweise nicht der Fall ist. Die Arten von Interaktionen in diesen beiden Arten von Benutzeroberflächen sind sehr unterschiedlich und erstellen einen Moderator, den Sie in allen Situationen verwenden können, möglicherweise nicht so ein klarer Schnitt.
Abbildung 9 - Windows forms Application Demo

Abbildung 10 - Aktualisierte Lösung

Für diesen Artikel wurde die klassische Interpretation des MVP -Musters verwendet und durch die Implementierung einer ASP.NET -Lösung demonstriert. Das ursprüngliche MVP -Muster gilt seit Martin Fowler "pensioniert", seit Martin Fowler dies angekündigt hat. Das Muster kann jetzt in zwei Lager aufgeteilt werden, da es sich um:
Lesen Sie diesen Artikel von Microsoft Patterns & Practices für weitere Informationen.
Ich habe diesen Artikel geschrieben, um eine konkrete Implementierung des MVP -Musters zu liefern, als ich ihn zu dieser Zeit untersuchte. Es könnte jedoch ratsam sein, sich davon fernzuhalten und zu warten, bis Microsoft das ASP.NET -MVC -Framework veröffentlicht. Für diejenigen, die ein Model-View-Controller-Framework (MID-View-Controller) imitieren möchten, empfehle ich, diesen Artikel von Microsoft Muster und Praktiken zu lesen.
Bemerkung : Das MVP -Muster ist eine Ableitung des Modellansichts -Controller -Musters (MVC). Zum Zeitpunkt dieses Schreibens ist Microsoft derzeit damit beschäftigt, das ASP.NET -MVC -Framework zu entwickeln. Wenn Sie die Architektur für ein neues Website -Projekt erstellen, empfehle ich, dieses Framework zu überprüfen.
Der Hauptunterschied zwischen den MVP- und MVC -Mustern kann so genau angegeben werden, wer für die Behandlung der Benutzereingaben wie Tastatur- und MoUe -Ereignisse verantwortlich ist. Im MVP -Muster ist die GUI selbst verantwortlich und muss sie durch Ereignisse an den Moderator delegieren. Im MVC -Muster ist der Controller für den Umgang mit diesen Ereignissen verantwortlich.