Unternehmensmuster mit WCF RIA-Diensten
Michael D. Brown
Beispielcode herunterladen.
Zwei wichtige Ankündigungen von PDC09 und Mix10 waren die Verfügbarkeit von Silverlight 4 Beta bzw. RC. Wenn Sie diesen Artikel lesen, ist die Vollversion von Silverlight 4 über das Internet zum Download verfügbar. Zusammen mit der umfassenden Druckerunterstützung ist Unterstützung für erhöhte Berechtigungen, Webcams, Mikrofone, Toast, Zugriff auf die Zwischenablage usw. enthalten. Mit dem neuen Featuresatz zieht Silverlight 4 mit Adobe AIR als umfassendes UI-Framework für verschiedene Plattformen gleich.
Bạn đang xem: wcf ria services la gi
Zwar bin ich von allem beeindruckt, aber ich bin in erster Linie ein Entwickler von Geschäftsanwendungen und domain authority würde es mir besonders gut gefallen, wenn ich auf einfache Weise meine Geschäftsdaten und -logik in eine Silverlight-Anwendung einbinden könnte.
Ein Problem von Branchenanwendungen mit Silverlight ist die Verbindung mit den Daten. Sie können natürlich einen eigenen WCF-Dienst (Windows Communication Foundation) erstellen und damit in Silverlight 3 eine Verbindung herstellen. Aber sánh bleiben viele Wünsche offen, insbesondere wenn Sie an die unzähligen Wege denken, auf die Sie eine Verbindung mit Daten über ASP.NET- oder Desktopanwendungen herstellen können. Während Desktop- und Webanwendungen über NHibernate, Entity Framework (EF) oder einfache ADO.NET-Konstrukte eine Verbindung herstellen können, sind Silverlight-Anwendungen durch „die Cloud“ von den Daten getrennt. Ich nenne diese Trennung „Datenkluft“.
Die Überquerung dieser Kluft kann zunächst trügerisch leicht erscheinen. Sie wurde auch in gewissem Maße bereits in einer Reihe vorhandener Silverlight-Anwendungen mit vielen Daten vollzogen. Was aber zunächst wie eine einfache Aufgabe aussieht, wird immer komplizierter, wenn Sie sich mit mehr Problemen auseinander setzen. Wie verfolgen Sie Änderungen über das Netzwerk oder kapseln Geschäftslogik in Entitäten auf verschiedenen Seiten der Firewall? Wie halten Sie die Details der Übertragung aus dem Geschäftsbetrieb fern?
Es gibt immer mehr Tools von Drittanbietern für diese Probleme, aber auch Microsoft hat erkannt, dass eine Lösung bereitgestellt werden muss. Das Ergebnis sind WCF RIA-Dienste (ehemals .NET RIA-Dienste) oder kurz RIA-Dienste. (Sie finden eine vollständige Einführung in RIA-Dienste yên lặng Thema zum Erstellen einer datengesteuerten Ausgabenanwendung mit Silverlight 3 in der Ausgabe von Mai 2009 des MSDN-Magazins (msdn.microsoft.com/magazine/dd695920). Ich bin dabei, seit ich zum Betaprogramm eingeladen wurde, mache dem Entwicklungsteam Vorschläge und lerne, wie ich das Framework in meinen eigenen Anwendungen nutzen kann.
Eine in Foren zu RIA-Diensten häufig gestellte Frage ist, wie RIA-Dienste in die Architektur bewährter Methoden passen. Ich war schon immer von den grundlegenden Funktionen für „Formulare über Daten“ von RIA-Diensten beeindruckt, und natürlich habe ich die Gelegenheit erkannt, die Architektur meiner Anwendungen zu verbessern, sodass Frameworkprobleme keinen Einfluss auf die Logik meiner Anwendungen haben.
Einführung in KharaPOS
Ich habe eine Beispielanwendung, KharaPOS, entwickelt, um ein konkretes Beispiel der Konzepte bereitzustellen, die ich in diesem Artikel vorstellen werde. Es ist eine POS-Anwendung (Point Of Sale), die in Silverlight 4 mit RIA-Diensten, Entity Framework und SQL Server 2008 implementiert wurde. Das endgültige Ziel ist, dass die Anwendung auf der Windows Azure-Plattform und in SQL Azure gehostet werden kann. Es besteht aber noch das kleine Problem der fehlenden Unterstützung von Microsoft .NET Framework 4 bei der Windows Azure-Plattform.
In der Zwischenzeit stellt KharaPOS ein gutes Beispiel für die Verwendung von .NET Framework 4 zum Erstellen einer echten Anwendung dar. Das Projekt wird über CodePlex unter KharaPOS.codeplex.com gehostet. Sie können die Website besuchen, um den Code herunterzuladen, die Dokumentation anzusehen und an der Diskussion über die Entwicklung der Anwendung teilzuhaben.
Ich sollte erwähnen, dass ich das Buch „Object Models: Strategies, Patterns, and Applications, Second Edition“ (Prentice Hall PTR, 1996) von Peter Coad mit David North und Mark Mayfield für den Großteil des Entwurfs und der Funktionalität der Anwendung KharaPOS genutzt habe. Ich konzentriere mich auf ein Teilsystem der Anwendung, die Katalogverwaltung (siehe Abbildung 1).
Abbildung 1 Das Entity Data Model für die Katalogverwaltung
Unternehmensmuster
In einer Reihe hervorragender Bücher werden die Entwurfsmuster für die Entwicklung von Unternehmensanwendungen erläutert. Ich verwende ständig das Buch „Patterns für Enterprise Application-Architekturen“ (Mitp-Verlag, 2003) von Martin Fowler als Referenz. Dieses Buch und die ergänzende Website (martinfowler.com/eaaCatalog/) bieten eine exzellente Zusammenfassung nützlicher Softwaremuster für die Entwicklung von Unternehmensanwendungen.
Einige Muster in Fowlers Katalog behandeln die Darstellung und Bearbeitung von Daten. Und es ist interessant, dass sie den gleichen Raum beanspruchen wie RIA-Dienste. Wenn Sie die Muster verstehen, bekommen Sie ein klareres Bild davon, wie RIA-Dienste an den Bedarf aller Geschäftsanwendungen – von der einfachsten bis zur kompliziertesten – angepasst werden können. Ich werde die folgenden Muster erläutern:
- Formulare und Steuerelemente
- Transaktionsskript
- Domänenmodell
- Anwendungsdienstebene
Sehen wir uns diese Muster kurz an. Die ersten drei betreffen unterschiedliche Arten, mit der Logik, die die Daten umgibt, umzugehen. Nach und nach ändert sich die Logik. Erst ist sie in der Anwendung verteilt und wird bei Bedarf wiederholt, und am Ende ist die zentralisiert und fokussiert.
Formulare und Steuerelemente
Beim Muster „Formulare und Steuerelemente“ (oder „Formulare über Daten“ wie ich es nenne) ist die gesamte Logik in der UI enthalten. Auf den ersten Blick sieht dies nach keiner guten Idee aus. Aber für einfache Datenerfassungs- und Masterdetailansichten ist es der einfachste und direkteste Ansatz, von der UI auf die Datenbank zuzugreifen. Viele Frameworks weisen eine spezifische Unterstützung für dieses Muster auf (Ruby on Rails, ASP.NET Dynamic Data und SubSonic sind die drei wichtigsten Beispiele). Es gibt also definitiv die Zeit und den Ort für das, was von einigen als Antimuster bezeichnet wird. Obwohl viele Entwickler mit dem Ansatz „Formulare über Daten“ nur die anfänglichen Prototypen erstellen, gibt es bestimmte Einsatzbereiche dafür in endgültigen Anwendungen.
Unabhängig von Ihrer Meinung zur Nutzbarkeit kann die Einfachheit und Zugänglichkeit von „Formulare über Daten“ nicht bestritten werden. Es wird nicht als schnelle Anwendungsentwicklung (Rapid Application Development, RAD) bezeichnet, weil es mühsam ist. Mit WCF RIA-Diensten wird RAD in Silverlight umgesetzt. Durch die Nutzung von Entity Framework, RIA-Diensten und dem Silverlight-Designer kann ein einfacher Formulare-über-Daten-Editor in fünf Schritten für eine Datenbanktabelle erstellt werden:
- Erstellen Sie eine neue Silverlight-Geschäftsanwendung.
- Fügen Sie der erstellten Webanwendung ein neues Entity Data Model (EDM) hinzu (mit dem Assistenten zum Importieren der Datenbank).
- Fügen Sie der Webanwendung einen Domänendienst hinzu (erstellen Sie ihn zuerst, damit das EDM richtig ermittelt wird), der auf das Datenmodell verweist.
- Ziehen Sie aus dem Datenquellenpanel eine von RIA-Diensten verfügbar gemachte Entität auf die Oberfläche einer Seite oder eines Benutzersteuerelements in der Silverlight-Anwendung (Sie müssen den Buildvorgang wiederholen, damit der neue Domänendienst erkannt wird).
- Fügen Sie eine Schaltfläche und CodeBehind hinzu, um mit der folgenden einfachen Zeile Änderungen am Formular in der Datenbank zu speichern:
this.categoryDomainDataSource.SubmitChanges();
Jetzt haben Sie ein einfaches Datenraster, mit dem Sie vorhandene Zeilen in der Tabelle direkt bearbeiten können. Mit einigen weiteren Ergänzungen können Sie ein Formular erstellen, mit dem Sie der Tabelle neue Zeilen hinzufügen können.
Zwar wurde dieses Muster wiederholt vorgeführt, und die Vorteile von RAD mit WCF RIA-Diensten wurden veranschaulicht, es ist hier aber trotzdem relevant, domain authority es Basisdaten für die Entwicklung yên lặng Framework liefert. Wie erwähnt ist es zudem ein gültiges Muster in Anwendungen, die auf RIA-Diensten basieren.
Empfehlung Wie ASP.NET Dynamic Data sollte das Formular-über-Daten-Muster für einfache Verwaltungs-UIs (wie den Produktkategorie-Editor KharaPOS) verwendet werden, bei denen die Logik einfach und überschaubar ist: Hinzufügen, Entfernen und Bearbeiten von Zeilen in einer Nachschlagetabelle. Aber Silverlight und RIA-Dienste können in viel komplexeren Anwendungen genutzt werden, wie yên lặng Folgenden veranschaulicht wird.
Tabellendatengateway Der standardmäßige, sofort nutzbare Ansatz für RIA-Dienste-Anwendungen, den ich gerade erläutert habe, kann auch als Implementierung des Musters „Tabellendatengateway“ aus Fowlers Buch gesehen werden. Über zwei Dereferenzierungsebenen (EF-Zuordnung über die Datenbank gefolgt von der Domänendienstzuordung über EF) habe ich mit den grundlegenden Vorgängen Erstellen, Lesen, Aktualisieren und Löschen (Create, Read, Update and Delete, CRUD) ein einfaches Gateway zur Datenbank erstellt, und es werden stark typisierte Datentransferobjekte (DTOs) zurückgegeben.
Technisch ist dies aufgrund der zwei Dereferenzierungsebenen kein reines Tabellendatengateway. Es sieht aber fast wie das Muster „Tabellendatengateway“ aus. Um ehrlich zu sein, wäre der logischere Ablauf gewesen, die Zuordnung zwischen RIA-Diensten und dem Muster „Tabellendatengateway“ zu besprechen, domain authority die übrigen Muster in der Liste Datenschnittstellen-Muster sind, „Formulare über Dateien“ ist jedoch vorrangig ein UI-Muster. Mir erschien es aber klüger, mit dem einfachen Szenario zu beginnen und den Schwerpunkt auf die UI zu legen, die wieder in die Datenbank eingeht.
Model-View-ViewModel (MVVM) Auch wenn es einfach ist, ein funktionales Formular mit „Formulare über Daten“ zu erstellen, gibt es einige Schwierigkeiten. Abbildung 2, XAML für die Kategorieverwaltung, veranschaulicht dies.
Abbildung 2 XAML für die Kategorieverwaltung
<Controls:TabItem Header="Categories">
<Controls:TabItem.Resources>
<DataSource:DomainDataSource
x:Key="LookupSource"
AutoLoad="True"
LoadedData="DomainDataSourceLoaded"
QueryName="GetCategoriesQuery"
Width="0">
<DataSource:DomainDataSource.DomainContext>
<my:CatalogContext />
</DataSource:DomainDataSource.DomainContext>
</DataSource:DomainDataSource>
<DataSource:DomainDataSource
x:Name="CategoryDomainDataSource"
AutoLoad="True"
LoadedData="DomainDataSourceLoaded"
QueryName="GetCategoriesQuery"
Width="0">
<DataSource:DomainDataSource.DomainContext>
<my:CatalogContext />
</DataSource:DomainDataSource.DomainContext>
<DataSource:DomainDataSource.FilterDescriptors>
<DataSource:FilterDescriptor
PropertyPath="Id"
Operator="IsNotEqualTo" Value="3"/>
</DataSource:DomainDataSource.FilterDescriptors>
</DataSource:DomainDataSource>
</Controls:TabItem.Resources>
<Grid>
<DataControls:DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Data,
Source={StaticResource CategoryDomainDataSource}}"
x:Name="CategoryDataGrid">
<DataControls:DataGrid.Columns>
<DataControls:DataGridTextColumn
Binding="{Binding Name}" Header="Name" Width="100" />
<DataControls:DataGridTemplateColumn
Header="Parent Category" Width="125">
<DataControls:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source=
{StaticResource LookupSource}, Path=Data}"
SelectedValue="{Binding ParentId}"
SelectedValuePath="Id"
DisplayMemberPath="Name"/>
</DataTemplate>
</DataControls:DataGridTemplateColumn.CellEditingTemplate>
<DataControls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Parent.Name}"/>
</DataTemplate>
</DataControls:DataGridTemplateColumn.CellTemplate>
</DataControls:DataGridTemplateColumn>
<DataControls:DataGridTextColumn
Binding="{Binding ShortDescription}"
Header="Short Description" Width="150" />
<DataControls:DataGridTextColumn
Binding="{Binding LongDescription}"
Header="Long Description" Width="*" />
</DataControls:DataGrid.Columns>
</DataControls:DataGrid>
</Grid>
</Controls:TabItem>
Die Spalte für die übergeordnete Kategorie yên lặng Datenraster ist ein Kombinationsfeld, in dem eine Liste vorhandener Kategorien verwendet wird, damit der Benutzer die übergeordnete Kategorie anhand des Namens auswählen kann und sich nicht an die Kategorie-ID erinnern muss. Leider kann in Silverlight dasselbe Objekt nicht zweimal in der grafischen Struktur geladen werden. Deshalb musste ich zwei Domänendatenquellen deklarieren: eine für das Raster und eine für das Suchkombinationsfeld. Zudem ist der CodeBehind für das Verwalten von Kategorien recht verschachtelt (siehe Abbildung 3).
Abbildung 3 CodeBehind für das Verwalten von Kategorien
private void DomainDataSourceLoaded(object sender, LoadedDataEventArgs e)
{
if (e.HasError)
{
MessageBox.Show(e.Error.ToString(), "Load Error", MessageBoxButton.OK);
e.MarkErrorAsHandled();
}
}
private void SaveButtonClick(object sender, RoutedEventArgs e)
{
CategoryDomainDataSource.SubmitChanges();
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
CategoryDomainDataSource.Load();
}
void ReloadChanges(object sender, SubmittedChangesEventArgs e)
{
CategoryDomainDataSource.Load();
}
Ich werde hier kein komplettes Lernprogramm zu MVVM abhalten. Im Artikel zu WPF-Anwendungen mit dem Entwurfsmuster „Model-View-ViewModel“ in der Ausgabe vom Februar 2009 (msdn.microsoft.com/magazine/dd419663) finden Sie eine hervorragende Abhandlung des Themas. Abbildung 4 zeigt eine Möglichkeit, MVVM in einer RIA-Dienste-Anwendung zu nutzen.
Abbildung 4 Kategorieverwaltung über ein Ansichtsmodell
public CategoryManagementViewModel()
{
_dataContext = new CatalogContext();
LoadCategories();
}
private void LoadCategories()
{
IsLoading = true;
var loadOperation= _dataContext.Load(_dataContext.
GetCategoriesQuery());
loadOperation.Completed += FinishedLoading;
}
protected bool IsLoading
{
get { return _IsLoading; }
set
{
_IsLoading = value;
NotifyPropertyChanged("IsLoading");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged!=null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
void FinishedLoading(object sender, EventArgs e)
{
IsLoading = false;
AvailableCategories=
new ObservableCollection<Category>(_dataContext.Categories);
}
public ObservableCollection<Category>AvailableCategories
{
get
{
return _AvailableCategories;
}
set
{
_AvailableCategories = value;
NotifyPropertyChanged("AvailableCategories");
}
}
Wie Sie sehen, initialisiert ViewModel den Domänenkontext und informiert die UI über Ladevorgänge. Hinzu kommt die Verarbeitung von Anforderungen der UI zur Erstellung neuer Kategorien, zum Speichern von Änderungen an vorhandenen Kategorien und zum erneuten Laden der Daten aus dem Domänendienst. Dies führt zu einer klaren Trennung zwischen der UI und der Logik, von der sie gesteuert wird. Das MVVM-Muster kann arbeitsaufwändiger aussehen, die Vorteile zeigen sich aber, wenn Sie zum ersten Mal die Logik ändern müssen, um Daten in die UI aufzunehmen. Das Verschieben des Ladevorgangs für die Kategorien in ViewModel ermöglicht es zudem, die Ansicht beträchtlich zu bereinigen (XAML, CodeBehind usw.).
Empfehlung Verwenden Sie MVVM, damit komplexe UI-Logik nicht Ihre UI oder sogar Ihr Geschäftsobjektmodell verstopft.
Transaktionsskript
Wenn Sie damit beginnen, Ihre Anwendung um Logik zu erweitern, wird das Formulare-über-Daten-Muster umständlich. Da die Logik, die sich darauf bezieht, was mit den Daten gemacht werden kann, in die UI eingebettet ist (oder in ViewModel, sofern Sie diesen Schritt gemacht haben), ist sie über die Anwendung verteilt. Ein weiterer Nebeneffekt der dezentralen Logik ist, dass sich die Entwickler möglicherweise nicht bewusst sind, dass bestimmte Funktionen in der Anwendung bereits vorhanden sind, was zu doppelter Arbeit führen kann. Dies sogt für Alpträume, wenn sich die Logik ändert, domain authority sie an allen Positionen geändert werden muss (vorausgesetzt, dass die Positionen, an denen die Logik implementiert wurde, ordnungsgemäß katalogisiert wurden).
Das Transaktionsskript-Muster (auch in Fowlers Buch erläutert) bietet eine gewisse Erleichterung. Damit können Sie die Geschäftslogik, die die Daten verwaltet, von der UI trennen.
Wie von Fowler definiert, strukturiert das Transaktionsskript die Geschäftslogik nach Prozeduren, wobei jede Prozedur eine Anforderung der Darstellungsebene verarbeitet. Transaktionsskripts sind viel mehr als einfache CRUD-Vorgänge. Tatsächlich befinden sie sich vor dem Tabellendatengateway, um CRUD-Vorgänge zu verarbeiten. Übertrieben ausgedrückt, wird jeder Vorgang zum Abrufen und Einfügen von Daten aus der und in die Datenbank von einem separaten Transaktionsskript ausgeführt. Da wir aber logisch denkende Menschen sind, wissen wir, dass es für alles eine Zeit und einen Ort gibt.
Ein Transaktionsskript ist nützlich, wenn die Anwendung die Interaktion zwischen zwei Entitäten koordinieren muss, beispielsweise wenn Sie eine Zuordnung zwischen zwei Instanzen unterschiedlicher Entitätsklassen erstellen. Im Katalogverwaltungssystem kennzeichne ich z. B., das ein Produkt zur Bestellung von einer Geschäftseinheit für den Bestand verfügbar ist, indem ein Katalogeintrag erstellt wird. Dieser Eintrag identifiziert das Produkt, die Geschäftseinheit, eine Produkt-SKU und die Zeit, in der es intern und extern bestellt werden kann. Um die Erstellung von Katalogeinträgen zu vereinfachen, habe ich yên lặng Domänendienst eine Methode erstellt (wie yên lặng folgenden Codeausschnitt veranschaulicht), die ein Transaktionsskript zum Ändern der Produktverfügbarkeit für eine Geschäftseinheit bereitstellt, ohne dass die UI die Katalogeinträge direkt ändern muss.
Tatsächlich werden die Katalogeinträge durch den Domänendienst nicht einmal verfügbar gemacht, wie hier gezeigt:
public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
var entry = ObjectContext.CreateObject<CatalogEntry>();
entry.BusinessUnitId = businessUnitId;
entry.ProductId = product.Id;
entry.DateAdded = DateTime.Now;
ObjectContext.CatalogEntries.AddObject(entry);
ObjectContext.SaveChanges();
}
Statt als Funktion yên lặng Domänenkontext des Clients verfügbar gemacht zu werden, generieren RIA-Dienste eine Funktion für die fragliche Entität (in diesem Fall das Produkt), die, wenn sie aufgerufen wird, eine Änderungsbenachrichtigung auf dem Objekt platziert, die auf dem Server als Aufruf der Methode yên lặng Domänendienst interpretiert wird.
Xem thêm: t ara thuộc công ty nào
Fowler empfiehlt zwei Ansätze für die Implementierung des Transaktionsskripts:
- Mit Befehlen, die die Vorgänge kapseln und weitergegeben werden können
- Mit einer Klasse, die eine Sammlung von Transaktionsskripts enthält
Ich bin hier nach dem zweiten Ansatz vorgegangen, sie können aber auch Befehle verwenden. Der Vorteil, den Katalogeintrag nicht auf der UI-Ebene verfügbar zu machen, ist, dass dann nur mit dem Transaktionsskript Katalogeinträge erstellt werden können. Wenn Sie das Befehlsmuster verwenden, wird die Regel üblicherweise durchgesetzt. Wenn ein Entwickler das Vorhandensein eines Befehls vergisst, haben Sie wieder das Problem der Fragmentierung und Doppelung von Logik.
Ein weiterer Vorteil, dass Transaktionsskript in den Domänendienst aufzunehmen, ist, dass die Logik dann auf dem Server ausgeführt wird (wie bereits erwähnt). Wenn Sie über proprietäre Algorithmen verfügen oder sicher sein möchten, dass der Benutzer die Daten nicht unrechtmäßig bearbeitet hat, sollten Sie das Transaktionsskript in den Domänendienst einbinden.
Empfehlung Verwenden Sie das Transaktionsskript, wenn Ihre Geschäftslogik für „Formulare über Daten“ zu komplex wird, Sie die Logik für einen Vorgang auf dem Server ausführen möchten oder in beiden Fällen.
Geschäftslogik oder UI-Logik Ich habe mehrfach UI-Logik und Geschäftslogik in Zusammenhang gesetzt, und auch wenn die Unterscheidung spitzfindig erscheinen mag, sie ist wichtig. UI-Logik ist die Logik, die sich mit der Darstellung beschäftigt: Was wird auf dem Bildschirm angezeigt und wie (beispielsweise die Elemente, mit denen das Kombinationsfeld aufgefüllt wird). Die Geschäftslogik steuert dagegen die Anwendung selbst (beispielsweise der Rabatt, der auf einen Onlinekauf angewendet wird). Beides sind wichtige Facetten einer Anwendung, und wenn beide gemischt werden, entsteht ein neues Muster. Informationen dazu finden Sie yên lặng Artikel von Brian Foote und Joseph Yoder unter laputan.org/mud.
Übergeben mehrerer Entitäten an den Domänendienst Standardmäßig können Sie nur eine Entität an eine benutzerdefinierte Domänendienstmethode übergeben. Beispielsweise funktioniert die Methode
public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
nicht, wenn Sie versuchen, eine Signatur statt einer ganzen Zahl zu verwenden:
public void CatalogProductForBusinessUnit(Product product, BusinessUnit bu)
RIA-Dienste würden für diese Funktion keinen Clientproxy erstellen, weil die Regeln anders lauten. In einer benutzerdefinierten Dienstmethode ist nur eine Entität möglich. Dies sollte in den meisten Situationen kein Problem darstellen, wenn Sie nämlich über eine Entität verfügen, haben Sie ihren Schlüssel und können sie wieder vom Back-End abrufen.
Nehmen wir zu Demonstrationszwecken an, dass das Abrufen einer Entität aufwändig ist (möglicherweise befindet sie sich am anderen Ende eines Webdiensts). Sie können dem Domänendienst dann mitteilen, dass er eine Kopie einer bestimmten Entität enthalten soll, wie hier veranschaulicht:
public void StoreBusinessUnit(BusinessUnit bu)
{
HttpContext.Current.Session[bu.GetType().FullName+bu.Id] = bu;
}
public void CatalogProductForBusinessUnit(Product product, int businessUnitId)
{
var currentBu = (BusinessUnit)HttpContext.Current.
Session[typeof(BusinessUnit).FullName + businessUnitId];
// Use the retrieved BusinessUnit Here.
}
Da der Domänendienst unter ASP.NET ausgeführt wird, verfügt er über vollständigen Zugriff auf die ASP.NET-Sitzung und auch auf den Cache, falls Sie das Objekt nach einer bestimmten Zeitspanne automatisch aus dem Arbeitsspeicher entfernen möchten. Ich verwende diese Technik tatsächlich bei einem Projekt, bei dem ich CRM-Daten (Customer Relationship Management, Geschäftsbeziehungsmanagement) aus mehreren Remotewebdiensten abrufen und in einer vereinheitlichten UI darstellen muss. Ich nutze eine explizite Methode, domain authority einige, aber nicht alle Daten zwischengespeichert werden sollen.
Domänenmodell
Manchmal wird die Geschäftslogik sánh komplex, dass sie auch mit Transaktionsskripts nicht mehr richtig verwaltet werden kann. Dies zeigt sich häufig in komplexer Verzweigungslogik innerhalb eines oder mehrerer Transaktionsskripts, mit der Nuancen in der Logik abgedeckt werden. Ein weiteres Anzeichen dafür, dass eine Anwendung der Nutzbarkeit von Transaktionsskripts entwachsen ist, ist die Notwendigkeit häufiger Updates, um sich schnell ändernde Geschäftsanforderungen zu berücksichtigen.
Wenn Sie diese Symptome festgestellt haben, ist es an der Zeit, über ein umfassendes Domänenmodell nachzudenken (es wird auch in Fowlers Buch beschrieben). Die bisher behandelten Muster haben eine Gemeinsamkeit: Die Entitäten sind einfache DTOs: Sie enthalten keine Logik (dies wird von einigen als Antimuster mit dem Namen „Anemic Domain Model“ bezeichnet). Zu den wichtigsten Vorteilen der objektorientierten Entwicklung gehört die Fähigkeit, Daten und die zugehörige Logik gemeinsam zu kapseln. Ein umfassendes Domänenmodell nutzt diesen Vorteil, indem die Logik wieder in die Entität aufgenommen wird, in die sie gehört.
Die Einzelheiten für den Entwurf eines Domänenmodells werden in diesem Artikel nicht behandelt. Umfassendere Informationen zu diesem Thema finden Sie yên lặng Buch „Domain-Driven Design: Tackling Complexity in the Heart of Software“ (Addison-Wesley, 2004) von Eric Evans oder yên lặng bereits erwähnten Buch von Peter Coad über Objektmodelle. Ich kann aber ein Szenario anbieten, das veranschaulicht, wie ein Domänenmodell bei der Bewältigung dieser Aufgabe helfen kann.
Einige KharaPOS-Kunden möchten sich die Umsätze bestimmter Linien in der Vergangenheit ansehen und dann auf Grundlage des Markts entscheiden, ob diese Linien erweitert (mehr Produkte der Linien werden angeboten), verringert, gänzlich gestrichen oder für eine bestimmte Saison unverändert belassen werden sollen.
Die Umsatzdaten liegen bereits in einem anderen Teilsystem von KharaPOS vor. Alle Daten, die ich sonst noch benötige, sind yên lặng Katalogsystem. Ich nehme einfach eine schreibgeschützte Sicht der Produktumsätze in das Entity Data Model auf, wie in Abbildung 5 veranschaulicht.
Abbildung 5 Mit Umsatzdaten aktualisiertes Entity Data Model
Nun muss ich nur noch dem Domänenmodell die Logik für die Produktauswahl hinzufügen. Da ich Produkte für einen Markt auswähle, platziere ich die Logik in der BusinessUnit-Klasse (verwenden Sie eine partielle Klasse mit der Erweiterung „shared.cs“ oder „shared.vb“, um den RIA-Diensten mitzuteilen, dass diese Funktion an den Client übermittelt werden soll). Abbildung 6 zeigt den Code.
Abbildung 6 Domänenlogik für die Auswahl von Produkten für eine Geschäftseinheit
public partial class BusinessUnit
{
public void SelectSeasonalProductsForBusinessUnit(
DateTime seasonStart, DateTime seasonEnd)
{
// Get the total sales for the season
var totalSales = (from sale in Sales
where sale.DateOfSale > seasonStart
&& sale.DateOfSale < seasonEnd
select sale.LineItems.Sum(line => line.Cost)).
Sum(total=>total);
// Get the manufacturers for the business unit
var manufacturers =
Catalogs.Select(c =>c.Product.ManuFacturer).
Distinct(new Equality<ManuFacturer>(i => i.Id));
// Group the sales by manufacturer
var salesByManufacturer =
(from sale in Sales
where sale.DateOfSale > seasonStart
&& sale.DateOfSale < seasonEnd
from lineitem in sale.LineItems
join manufacturer in manufacturers on
lineitem.Product.ManufacturerId equals manuFacturer.Id
select new
{
Manfacturer = manuFacturer,
Amount = lineitem.Cost
}).GroupBy(i => i.Manfacturer);
foreach (var group in salesByManufacturer)
{
var manufacturer = group.Key;
var pct = group.Sum(t => t.Amount)/totalSales;
SelectCatalogItemsBasedOnPercentage(manufacturer, pct);
}
}
private void SelectCatalogItemsBasedOnPercentage(
ManuFacturer manufacturer, decimal pct)
{
// Rest of logic here.
}
}
Die Ausführung der automatischen Auswahl von Produkten, die für eine Saison übertragen werden sollen, ist sánh einfach, wie das Aufrufen der neuen Funktion für die BusinessUnit-Klasse mit einem anschließenden Aufruf der SubmitChanges-Funktion für DomainContext. Wenn später in der Logik ein Fehler gefunden wird oder die Logik aktualisiert werden muss, weiß ich genau, wo ich suchen muss. Ich habe die Logik nicht nur zentralisiert, das Objektmodell bringt auch den Zweck besser zum Ausdruck. Im Buch „Domain-Driven Design“ erläutert Eric Evans auf Seite 246, welchen Vorteil das hat:
Wenn ein Entwickler die Implementierung einer Komponente erwägen muss, um sie verwenden zu können, geht der Nutzen der Kapselung verloren. Wenn jemand anderes als der ursprüngliche Entwickler den Zweck eines Objekts oder Vorgangs basierend auf der Implementierung ableiten muss, kann der neue Entwickler den Zweck des Vorgangs oder der Klasse nur zufällig ableiten. Wenn das nicht die Absicht war, funktioniert der Code möglicherweise für den Moment, aber die konzeptionelle Basis des Entwurfs ist beschädigt, und die beiden Entwickler arbeiten aneinander vorbei.
Anders ausgedrückt: Indem ich die Funktion nach ihrem Zweck benannt und die Logik gekapselt habe (zusammen mit einigen Kommentaren, die die Vorgänge erläutern), habe ich es für den nächsten Bearbeiter (auch wenn ich selbst es in fünf Monaten bin) einfach gemacht, das Geschehen zu bestimmen, ohne die Implementierung zu analysieren. Indem die Logik bei den Daten platziert wird, zu der sie gehört, wird der Vorteil der ausdrucksstarken Eigenschaften von objektorientierten Sprachen genutzt.
Empfehlung Verwenden Sie ein Domänenmodell, wenn die Logik komplex und verzweigt ist und gleichzeitig mehrere Entitäten betreffen kann. Bündeln Sie die Logik mit dem Objekt, zu dem sie am ehesten gehört, und geben Sie dem Vorgang einen aussagekräftigen, bewusst gewählten Namen.
Der Unterschied zwischen dem Domänenmodell und dem Transaktionsskript in RIA-Diensten Möglicherweise ist Ihnen aufgefallen, dass beim Transaktionsskript und dem Domänenmodell der Aufruf direkt für die Entität erfolgt ist. Beachten Sie jedoch, dass sich die Logik für die beiden Muster an unterschiedlichen Orten befindet. Beim Transaktionsskript dient der Aufruf der Funktion für die Entität nur dazu, für den Domänenkontext/-dienst anzugeben, dass die entsprechende Funktion für den Domänendienst aufgerufen werden soll, wenn das nächste Mal die Funktion zum Übermitteln von Änderungen aufgerufen wird. Beim Domänenmodell wird die Logik auf dem Client ausgeführt und dann übergeben, wenn die Funktion zum Übermitteln von Änderungen aufgerufen wird.
Das Repository und Abfrageobjekte Der Domänendienst implementiert natürlich das Repository-Muster (auch in Fowlers Buch erwähnt). Im Codekatalog von WCF RIA-Diensten (code.msdn.microsoft.com/RiaServices) hat das Team der RIA-Dienste ein sehr gutes Beispiel für die Erstellung einer expliziten Implementierung des Musters über DomainContext bereitgestellt. Damit können Sie Ihre Anwendung besser testen, ohne tatsächlich auf die Dienstebene und Datenbank zugreifen zu müssen. In meinem Blog (azurecoding.net/) stelle ich zudem eine Implementierung des Abfrageobjektmusters (aus Fowlers Buch) über das Repository bereit, mit der die Ausführung der Abfrage auf dem Server bis zur eigentlichen Enumeration ausgesetzt wird.
Anwendungsdienstebene
Kurze Frage: Was machen Sie, wenn Sie ein umfassendes Domänenmodell nutzen möchten, dessen Logik aber nicht der UI-Ebene verfügbar machen möchten? Hier kommt das Anwendungsdienstebene-Muster (aus Fowlers Buch) gelegen. Sobald Sie das Domänenmodell haben, kann dieses Muster leicht implementiert werden, indem Sie die Domänenlogik aus „shared.cs“ in eine separate partielle Klasse verschieben und eine Funktion yên lặng Domänendienst platzieren, die die Funktion für die Entität aufruft.
Die Anwendungsdienstebene agiert als eine vereinfachte Fassade vor dem Domänenmodell und macht Vorgänge, aber nicht deren Details verfügbar. Ein weiterer Vorteil ist, dass Ihre Domänenobjekte interne Abhängigkeiten annehmen können, ohne das die Clients der Dienstebene sie ebenfalls annehmen müssen. In einigen Fällen (wie yên lặng Beispiel für die Auswahl saisonaler Produkte in Abbildung 6) führt der Domänendienst einen einfachen Aufruf für die Domäne aus. Manchmal können aber auch einige Entitäten orchestriert werden. Seien Sie aber vorsichtig: Zu viel Orchestrierung führt wieder zu einem Transaktionsskript, und die Vorteile der gekapselten Logik in der Domäne gehen verloren.
Empfehlung Verwenden Sie die Anwendungsdienstebene, um eine einfache Fassade vor dem Domänenmodell bereitzustellen und um die Anforderung aufzuheben, dass die UI-Ebene die Abhängigkeiten der Entitäten übernehmen muss.
Weiterer Vorteil: Der gebundene Kontext
In den RIA-Foren fragen die Teilnehmer häufig: „Wie verteile ich meine sehr große Datenbank auf Domänendienste, damit sie besser verwaltet werden kann?“ Daran schließt sich die Frage an: „Wie gehe ich mit den Entitäten um, die in mehreren Domänendiensten vorhanden sein müssen?“ Anfangs habe ich gedacht, dass solche Dinge nicht notwendig sein sollten. Der Domänendienst sollte als Dienstebene über dem Domänenmodell agieren, und ein einzelner Domänendienst sollte als Fassade vor der gesamten Domäne dienen.
Bei meine Recherchen für diesen Artikel bin ich jedoch auf das Muster „gebundener Kontext“ gestoßen (Evans, Seite 336). Ich hatte schon vorher darüber gelesen, aber nicht daran gedacht, als ich ursprünglich diese Fragen beantwortet habe. Die Grundvoraussetzung für das Muster ist, dass es bei jedem großen Projekt mehrere Unterdomänen gibt. Nehmen Sie beispielsweise KharaPOS, wo es eine Domäne für Kataloge und eine separate Domäne für Umsätze gibt.
Mit dem gebundenen Kontext können diese Domänen friedlich nebeneinander vorliegen, auch wenn sie sich einige Elemente teilen (wie „Sale“, „Business Unit“, „Product“ und „LineItem“, die in der Umsatz- und der Katalogdomäne vorkommen). Unterschiedliche Regeln gelten für die Entitäten. Sie basieren darauf, welche Domäne mit ihnen interagiert („Sale“ und „LineItem“ sind in der Katalogdomäne schreibgeschützt). Eine abschließende Regel ist, dass Vorgänge nie Kontexte überqueren. Dies macht das Leben angenehm, domain authority Silverlight Transaktionen nicht über mehrere Domänendiente unterstützt.
Empfehlung Verwenden Sie gebundene Kontexte, um ein großes System in logische Teilsysteme zu unterteilen.
Xem thêm: chuyển tiền từ sacombank sang agribank mất bao lâu
Der einfache Erfolg
In diesen Artikel wurde veranschaulicht, wie RIA-Dienste mit nur wenig Aufwand die wichtigsten Unternehmensmuster unterstützen. Selten sind Frameworks sánh zugänglich und gleichzeitig flexibel genug, von ganz einfachen Datenerfassungsanwendungen mit Arbeitsblättern bis hin zu sehr komplexen Geschäftsanwendungen alles zu unterstützen, ohne dass für den Übergang viel Arbeit investiert werden muss. Dies ist der einfache Erfolg, den Brad Abrams in folgendem Blogbeitrag erwähnt: blogs.msdn.com/brada/archive/2003/10/02/50420.aspx.
Mike Brown ist Präsident und Mitbegründer von KharaSoft Inc., einem Technologieunternehmen, das sich auf Schulung, benutzerdefinierte Softwareentwicklung und Software als Dienst spezialisiert hat. Er ist ein ausgebildeter Technologiespezialist mit mehr als 14 Jahren an Erfahrungen in der Branche, hat mehrere MVP-Auszeichnungen bekommen, ist Mitbegründer der Benutzergruppe Indy Alt.NET (indyalt.net) und ein eingefleischter Bears-Fan!
Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Brad Abrams
Bình luận