Juni 2016
Band 31, Nummer 6
Dieser Artikel wurde maschinell übersetzt.
Reactive Framework – Skalieren asynchroner Client/Server-Verknüpfungen mit Reactive
Durch Peter Vogel | Juni 2016
Wie die asynchrone Verarbeitung in der Anwendungsentwicklung häufiger geworden ist, hat Microsoft .NET Framework eine Vielzahl von Tools abgerufen, die bestimmte asynchrone Entwurfsmuster zu unterstützen. Erstellen eine gut entworfene asynchronen Anwendung häufig hängt davon ab, das Entwurfsmuster, das Ihre Anwendung implementieren und anschließend auswählen die richtige Gruppe von .NET Komponenten erkennen.
In einigen Fällen erfordert die Übereinstimmung integrieren mehrere Komponenten von .NET. Stephen Cleary Artikel "Muster für asynchrone MVVM-Anwendungen: Befehle"(bit.ly/233Kocr), zeigt, wie das Model-View-ViewModel (MVVM) auf asynchrone Weise vollständig unterstützt. In anderen Fällen muss nur eine Komponente von .NET Framework unterstützt werden. Implementieren des Anbieter-Consumer-Musters mithilfe der BlockingCollection in behandelten meine VisualStudioMagazine.com praktische .NET Spalten 'Simple erstellen, zuverlässige asynchroner Apps mit BlockingCollection' (bit.ly/1TuOpE6), und "Erstellen komplexe asynchrone Clientanwendungen mit BlockingCollection" (bit.ly/1SpYyD4).
Ein weiteres Beispiel implementiert das Entwurfsmuster "Beobachter", um einen langwierigen Vorgang asynchron zu überwachen. In diesem Szenario funktioniert eine asynchrone Methode, die ein einzelnes Taskobjekt zurückgibt, da der Client häufig ein Streams von Ergebnissen zurückgegeben wird. Für diese Szenarien können Sie mindestens zwei Tools von .NET Framework nutzen: die ObservableCollection und reaktive Erweiterungen (Rx). Für einfache Lösungen, die ObservableCollection (zusammen mit Async und await-Schlüsselwörtern) müssen Sie. Allerdings bietet weitere "interessanten" und, vor allem, das ereignisgesteuerte Probleme, Rx eine bessere Kontrolle über den Prozess.
Das Muster definieren
Während des Observer-Musters in Entwurfsmuster für die Benutzeroberfläche häufig verwendet wird, einschließlich der Model-View-Controller (MVC), Model-View-Presenter (MVP) und MVVM – Benutzeroberflächen sollten als eine größere Anzahl von Szenarien, in denen das Beobachtermuster gilt, nur ein Szenario betrachtet werden. Die Definition des Observer-Musters (zitieren aus Wikipedia) ist: "Ein Objekt aufgerufen, den Betreff, [, der] eine Liste der abhängigen verwaltet, Beobachtern aufgerufen und benachrichtigt sie automatisch von Zustandsänderungen, in der Regel durch eine ihrer Methoden aufrufen."
Geht das Beobachtermuster Ergebnisse lang andauernde Prozesse an den Client abrufen, sobald die Ergebnisse verfügbar sind. Ohne eine Version des Observer-Musters müssen die Clients warten, bis das letzte Ergebnis verfügbar ist und dann haben alle Ergebnisse werden in einer einzelnen Pauschalbetrag. In einer zunehmend asynchrone Welt soll die Beobachter Ergebnisse parallel mit dem Client verarbeitet, sobald die Ergebnisse verfügbar sind. Um zu verdeutlichen, dass Sie sprechen verwende mehrere Benutzeroberflächen, die bei der Nutzung des Observer-Musters "Client" und "Server" anstelle von "Beobachter" und "Subject" im Rest dieses Artikels ich.
Probleme und Chancen
Es gibt mindestens drei Punkte und zwei Möglichkeiten, mit dem Observer-Muster. Das erste Problem ist das Problem ausgelaufen Listener: Viele Implementierungen des Observer-Musters erforderlich ist, die zum Speichern eines Verweises auf allen Clients. Daher können Clients gespeichert werden im Speicher durch den Server, bis der Server beendet wird. Dies ist offensichtlich eine optimale Lösung für ein langer Prozess in einem dynamischen System, in dem Clients eine Verbindung herstellen, und trennen häufig, nicht.
Das Problem ausgelaufen Listener ist jedoch nur ein Symptom für das zweite, größere Problem: Viele Implementierungen des Observer-Musters müssen den Server und den Client eng gekoppelt werden, erfordert der Server und der Client immer vorhanden ist. Zumindest sollte der Client in der Lage zu bestimmen, ob der Server vorhanden ist und nicht für die Verbindung auswählen; Darüber hinaus sollte der Server sein zu funktionieren, auch wenn keine Clients akzeptieren Ergebnisse vorhanden sind.
Das dritte Problem ist die Leistung beziehen: Wie lange der Server alle Clients benachrichtigt dauert? Leistung des Observer-Musters ist die Anzahl der Clients benachrichtigt werden, direkt betroffen. Daher besteht die Möglichkeit zur Verbesserung der Leistung in des Observer-Musters mit deren Hilfe des Clients präemptiv Filtern der Ergebnisse, die vom Server zurückgegeben. Dabei werden auch die Szenarien, in denen der Server generiert, mehr Ergebnisse (oder einer größeren Anzahl von Ergebnissen) als der Client interessiert ist: Der Client kann anzugeben, dass es nur in bestimmten Fällen benachrichtigt werden. Die zweite Möglichkeit der Leistung vorhanden ist, um zu erkennen, dass der Server hat keine Ergebnisse oder Ergebnisse erzeugt hat. Clients können zum Erwerb von Ressourcen, die erforderlich sind, um Serverereignisse, bis der Client ist garantiert, etwas zu verarbeiten, und Clients können diese Ressourcen freigeben, sobald sie wissen, dass die Verarbeitung des letzten Resultsets verarbeiten überspringen.
Aus der Beobachter für Veröffentlichen/Abonnieren
Diese Aspekte lohnen führt zu der zugehörigen Veröffentlichen/Abonnieren-Modell von einfachen Implementierungen des Observer-Musters. Veröffentlichen/Abonnieren des Observer-Musters in einer lose verknüpften Weise, mit dem Server implementiert, und Clients ausgeführt, selbst wenn die anderen zurzeit nicht verfügbar ist. In der Regel auch implementiert Veröffentlichen/Abonnieren clientseitig filtern, mit deren Hilfe des Clients auf bestimmte Themen/Kanäle ("Benachrichtigen über Bestellungen") oder verschiedene Arten von Inhalt ("Benachrichtigen über dringenden Anfragen") zugeordneten Attribute zu abonnieren.
Ein Problem bleibt jedoch bestehen. Alle Implementierungen des Observer-Musters in der Regel eng mehrere Clients und Server in einer bestimmten Nachrichtenformat. Ändern des Formats einer Nachricht in den meisten Veröffentlichen/Abonnieren-Implementierungen kann schwierig sein, da alle Clients aktualisiert werden müssen, um das neue Format verwenden.
In vielerlei Hinsicht ähnelt dies die Beschreibung eines serverseitigen Cursors in einer Datenbank. Um die Kosten der Übertragung zu minimieren, zurückgeben nicht der Datenbankserver Ergebnisse, wie jede Zeile abgerufen wird. Allerdings zurückgegeben nicht für große Rowsets, die Datenbank auch alle Zeilen in einem einzelnen Batch am Ende. Stattdessen werden der Datenbankserver in der Regel gibt Teilmengen von einem Cursor frei, auf dem Server häufig als diese Teilmengen verfügbar. Mit einer Datenbank müssen der Client und der Server gleichzeitig vorhanden sein: Der Datenbankserver kann ausgeführt werden, wenn keine Clients vorhanden sind. ein Client kann überprüfen, um festzustellen, ob der Server verfügbar ist und, falls nicht, entscheiden, wie (falls zutreffend) anderen möglich. Der Filtervorgang (SQL) ist auch sehr flexibel. Jedoch, wenn das Datenbankmodul das Format verwendet ändert, um Zeilen zurückzugeben, müssen, dann alle Clients zumindest, neu kompiliert werden.
Verarbeiten einen Cache von Objekten
Als meine Fallstudie für eine Implementierung der einfachen Beobachter-Muster betrachten verwende wie meine Server eine Klasse ich, die einen in-Memory-Cache von Rechnungen sucht. Diesem Server könnten Sie eine Auflistung aller dem Rechnungen am Ende der Verarbeitung zurück. Jedoch würde ich bevorzuge, damit den Client die Rechnungen einzeln und parallel auf dem Server Search-Prozess verarbeitet. Das heißt, vorzugsweise eine Version des Prozesses, der die Rechnung zurückgibt, wie es gefunden wird und der Client Verarbeiten von Rechnungen parallel mit der Suche nach der nächsten Rechnung.
Eine einfache Implementierung des Servers kann wie folgt aussehen:
private List<Invoice> foundInvoices = new List<Invoice>();
public List<Invoice> FindInvoices(decimal Amount)
{
foundInvoices.Clear();
Invoice inv;
// ...search logic to add invoices to the collection
foundInvoices.Add(inv);
// ...repeat until all invoices found
return foundInvoices;
}
Ausgereiftere Projektmappen können Yield return Rechnungen zurückgeben, wie er gefunden wird, anstatt die Liste zusammenstellen. Unabhängig davon wird ein Client, der die FindInvoices-Methodenaufrufe einige wichtigen Aktivitäten vor und nach der Verarbeitung ausführen möchten. Z. B. nach das erste Element gefunden wird, kann der Client aktivieren möchten eine Liste MatchingInvoices halten die Rechnungen auf dem Client oder eine Rechnung verarbeiten erforderlichen Ressourcen abzurufen/initialisiert werden. Hinzufügen von zusätzliche Rechnungen müssten der Client verarbeiten jeder Rechnung und, wenn der Server signalisiert, dass die letzte Rechnung abgerufen wird, alle Ressourcen frei, die nicht mehr benötigt werden, da "mehr" Rechnungen zu verarbeiten sind.
Beim Abrufen der Datenbank z. B. blockiert ein Lesevorgang, bis die erste Zeile zurückgegeben wird. Sobald die erste Zeile zurückgegeben wird, initialisiert den Client Ressourcen benötigt werden, um eine Zeile zu verarbeiten. Der Lesevorgang gibt auch false zurück, wenn die letzte Zeile abgerufen wird, lassen den Client, die diese Ressourcen freigeben, da es keine weiteren Zeilen zu verarbeiten sind.
Erstellen einfache Lösungen mit ObservableCollection
Die offensichtliche Wahl zum Implementieren des Observer-Musters in .NET Framework ist der ObservableCollection. Der ObservableCollection benachrichtigt den Client (durch ein Ereignis) jedes Mal, wenn es geändert wird.
Umschreiben von meinem Beispielserver zur Verwendung der ObservableCollection-Klasse sind nur zwei Änderungen erforderlich. Zuerst muss die Auflistung mit den Ergebnissen als ObservableCollection definiert und veröffentlicht werden. Zweitens ist es nicht mehr erforderlich, für die Methode ein Ergebnis zurückgibt: Der Server muss nur Rechnungen in der Auflistung hinzu.
Die neue Implementierung des Servers kann wie folgt aussehen:
public List<Invoice> FindInvoices(decimal Amount)
{
public ObservableCollection<Invoice> foundInvoices =
new ObservableCollection<Invoice>();
public void FindInvoices(decimal Amount)
{
foundInvoices.Clear();
Invoice inv;
// ...search logic to set inv
foundInvoices.Add(inv);
// ...repeat until all invoices are added to the collection
}
Ein Client, der diese Version des Servers muss nur, um einen Ereignishandler an das CollectionChanged-Ereignis der InvoiceManagement FoundInvoices Sammlung zu aktivieren. Im folgenden Code habe ich die Klasse implementiert die IDisposable-Schnittstelle, um das Ereignis trennen zu unterstützen:
public class SearchInvoices: IDisposable
{
InvoiceManagement invMgmt = new InvoiceManagement();
public void SearchInvoices()
{
invMgmt.foundInvoices.CollectionChanged += InvoicesFound;
}
public void Dispose()
{
invMgmt.foundInvoices.CollectionChanged -= InvoicesChanged;
}
Im-Client wird das CollectionChanged-Ereignis ein NotifyCollectionChangedEventArgs-Objekt als zweiten Parameter übergeben. Dass die Action-Eigenschaft des Objekts gibt sowohl welche Änderung für die Auflistung ausgeführt wurde (die Aktionen sind: die Auflistung wurde gelöscht, neue Elemente zur Auflistung hinzugefügt wurden, vorhandene Elemente wurden verschoben/ersetzen/entfernt) und Informationen zu den geänderten Elementen (eine Auflistung von hinzugefügten Elemente, eine Auflistung von Elementen in der Auflistung vor dem neue Elemente hinzugefügt wird, wird die Position des Elements, das verschoben/entfernt/ersetzt wurde).
Einfacher Code im Client, die jeder Rechnung asynchron verarbeitet, der die Auflistung auf dem Server hinzugefügt wird sieht der Code in Abbildung 1.
Abbildung 1 asynchronen Verarbeitung von Rechnungen mithilfe von ObservableCollection
private async void InvoicesFound(object sender,
NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Reset:
{
// ...initial item processing
return;
}
case NotifyCollectionChangedAction.Add:
{
foreach (Invoice inv in e.NewItems)
{
await HandleInvoiceAsync(inv);
}
return;
}
}
}
Einfach, während dieser Code möglicherweise auf Ihre Bedürfnisse unzureichend ist, insbesondere, wenn Sie einen lang andauernden Prozesses behandeln oder in einer dynamischen Umgebung arbeiten. Aus Sicht von einer asynchronen Entwurf könnte z. B. der Code erfassen das Task-Objekt, das von der HandleInvoiceAsync zurückgegeben werden, sodass der Client die asynchronen Aufgaben verwalten. Sie sollten auch sicherstellen, dass das CollectionChanged-Ereignis für den UI-Thread ausgelöst wird, selbst wenn FindInvoices in einem Hintergrundthread ausgeführt wird.
Aufgrund, wenn die Clear-Methode in der Serverklasse aufgerufen wird, (unmittelbar vor der Suche nach der ersten Rechnung) kann der Aktion den Wert der Eigenschaft zurücksetzen als Signal verwendet werden, der das erste Element abgerufen werden sollen. Allerdings können natürlich keine Rechnungen in der Suche gefunden werden also die zurücksetzen-Aktion mit der Client beim Zuordnen von Ressourcen, die nie verwendet werden. Behandeln Sie tatsächlich "ersten Item" Verarbeitung müssten Sie die Aktion hinzufügen-Verarbeitung ausführen, wenn nur das erste Element gefunden wurde ein Flag hinzugefügt.
Darüber hinaus muss der Server eine begrenzte Anzahl von Optionen zur Angabe, die die letzte Rechnung gefunden wird, damit der Client beenden kann warten "Weiter." Könnten Sie vermutlich, löschen Sie den der Auflistung nach dem letzten Element suchen, aber gezwungen, die nur eine komplexere Verarbeitung in die Verarbeitung zurücksetzen-Aktion (haben ich wurde Verarbeitung von Rechnungen? Wenn Ja, habe ich die letzten Rechnung verarbeitet. Wenn nicht, kann ich dann über den Prozess der ersten Rechnung).
Während bei einfachen Problemen ObservableCollection in Ordnung sein wird, geht keine einigermaßen komplexe Implementierung basierend auf ObservableCollection (und jede Anwendung, die Effizienz Werte) komplizierten Code, insbesondere in den Client erforderlich ist.
Rx-Lösungen
Wenn Sie asynchrone möchten kann die Verarbeitung Rx (verfügbar über NuGet) eine bessere Lösung für das Implementieren des Observer-Musters durch Kreditaufnahme aus dem Veröffentlichen/Abonnieren-Modell bereitstellen. Diese Lösung bietet auch eine LINQ-basierten Filtern Modell zum ersten bzw. letzten Element Bedingung und eine bessere Fehlerbehandlung besser signalisieren.
Rx bewältigen auch interessantere Beobachter Implementierungen als ObservableCollection möglich sind. In meinem Fallstudie möglicherweise nach der Rückkehr von der ursprünglichen Liste von Rechnungen, meinen Server weiterhin neue Rechnungen zu überprüfen, die dem Cache hinzugefügt werden, nachdem die ursprüngliche Suche abgeschlossen ist (und natürlich die Suchkriterien entsprechen). Wenn eine Rechnung Besprechung Kriterien wird angezeigt, der Client wird über das Ereignis benachrichtigt werden möchten, damit die neue Rechnung zur Liste hinzugefügt werden kann. Rx unterstützt diese Art von Ereignis-basierte Erweiterungen des Observer-Musters besser als ObservableCollection.
Es gibt zwei wichtige Schnittstellen in Rx für die Unterstützung des Observer-Musters. Die erste ist IObservable < T >, vom Server implementiert und eine einzelne Methode angeben: Abonnieren. Der Server die Subscribe-Methode implementieren, wird einen Verweis auf ein Objekt von einem Client übergeben werden. Behandeln Sie das Problem versäumte Listener gibt die Subscribe-Methode einen Verweis auf den Client für ein Objekt, das die IDisposable-Schnittstelle implementiert. Dieses Objekt können der Client um vom Server zu trennen. Wenn der Client getrennt, wird der Server Entfernen des Clients aus der internen Liste erwartet.
Die zweite ist die IObserver < T >-Schnittstelle, die vom Client implementiert werden muss. Diese Schnittstelle muss der Client implementiert und drei Methoden auf dem Server verfügbar machen: OnNext OnCompleted und OnError. Die entscheidende Methode hier OnNext, die vom Server verwendet wird, um eine Nachricht an dem Client übergeben wird (in meinem Fallstudie Nachricht wäre neue Rechnung-Objekte, die jeweils angezeigten zurückgegeben werden). OnCompleted-Methode des Clients können der Server um zu signalisieren, dass keine Daten mehr vorhanden ist. Die dritte Methode OnError, bietet eine Möglichkeit für den Server an den Client zu signalisieren, dass eine Ausnahme aufgetreten ist.
Sie können die IObserver-Schnittstelle implementieren, natürlich (das ist Teil des .NET Framework). Zusammen mit der ObservableCollection vielleicht müssen Sie beim Erstellen eine synchrone Lösung (Ich habe eine Spalte geschrieben, zu diesem, "Schreiben saubereren Code mit Reactive Extensions" [bit.ly/10nfQtm]).
Die Rx verfügt jedoch über mehrere Pakete, die asynchrone Implementierungen dieser Schnittstellen, einschließlich Implementierungen für JavaScript und RESTful-Dienste bereitstellen. Die Betreff Rx-Klasse stellt eine Implementierung der IObservable, die die Implementierung einer asynchronen Veröffentlichen/Abonnieren-Version von des Observer-Musters vereinfacht.
Erstellen einer asynchronen Lösung
Erstellen einen Server mit einem Antragsteller-Objekt funktioniert erfordert nur wenige Änderungen an den ursprünglichen synchrone serverseitigen Code. Ich Ersetzen der alten ObservableCollection mit einem Antragsteller-Objekt, die jeder Rechnung listening-Clients übergeben werden, wie er angezeigt wird. Ich erkläre das Betreff-Objekt als öffentlich, damit Clients darauf zugreifen können:
public class InvoiceManagement
{
public IObservable<Invoice> foundInvoice =
new Subject<Invoice>();
Im Text der Methode, eine Rechnung zu einer Auflistung hinzufügen, anstatt verwende ich des Antragstellers OnNext-Methode, eine Rechnung an dem Client übergeben wird, wie er gefunden wird:
public void FindInvoices(decimal Amount)
{
inv = GetInvoicesForAmount(Amount) // Poll for invoices
foundInvoice.OnNext(inv);
// ...repeat...
}
In Mein Client deklariere ich zunächst eine Instanz der-Klasse. Dann rufe ich in einer Methode als asynchron markierten des Antragstellers Subscribe-Methode, um anzugeben, dass das Abrufen von Nachrichten gestartet werden soll:
public class InvoiceManagementTests
{
InvoiceManagement invMgmt = new InvoiceManagement();
public async void ProcessInvoices()
{
invMgmt.foundInvoice.Subscribe<Invoice>();
Zum Filtern der Ergebnisse, die nur die Rechnungen, werden soll, können eine LINQ-Anweisung das Betreff-Objekt zugewiesen werden. In diesem Beispiel filtert die Rechnungen auf diejenigen, die wieder angeordnet sind (Rx LINQ-Erweiterungen verwenden, Sie eine mit hinzufügen müssen,-Anweisung für den Namespace System.Reactive.Linq):
invMgmt.foundInvoice.Where(i => i.BackOrder == "BackOrder").Subscribe();
Nachdem ich Lauschen auf das Subjekt gestartet haben, kann ich angeben, welche Verarbeitung tun, wenn ich eine Rechnung empfangen werden soll. Ich, z. B. "firstasync" können Sie nur die erste Rechnung, die vom Dienst zurückgegebenen verarbeiten. In diesem Beispiel verwende ich die Await-Anweisung mit dem Aufruf von "firstasync", damit ich Steuerelement an den Hauptteil der Anwendung während der Verarbeitung der Rechnung zurückgeben kann. Dieser Code wartet zum Abrufen dieser ersten Rechnung und dann springt auf, die ich verwenden, um die Rechnung verarbeiten Prozess zu initialisieren und schließlich die Rechnung verarbeitet der Code:
Invoice inv;
inv = await invMgmt.foundInvoice.FirstAsync();
// ...setup code invoices...
HandleInvoiceAsync(inv);
Es gibt jedoch einen Nachteil: "Firstasync" wird blockiert, wenn der Server noch keine Ergebnisse erzeugt noch nicht. Wenn Sie eine Blockierung zu vermeiden möchten, können Sie "firstordefaultasync", zurück, wenn der Server keine Ergebnisse erzeugt noch nicht null. Wenn keine Ergebnisse vorliegen, kann der Client entscheiden, wenn nichts damit zu tun.
Die üblicher ist, dass der Client alle Rechnungen zurückgegeben (nach dem Filtern) verarbeitet und asynchron ausführen möchte. In diesem Fall anstelle eine Kombination von abonnieren und OnNext können einfach die ForEachAsync-Methode Sie. Sie können eine Methode oder ein Lambda-Ausdruck, der eingehende Ergebnisse verarbeitet übergeben. Wenn Sie eine Methode (die asynchrone sein kann), als übergeben kann ich hier, dass die Methode die Rechnung übergeben werden, die ForEachAsync ausgelöst:
invMgmt.foundInvoice.ForEachAsync(HandleInvoice);
Die ForEachAsync-Methode, kann auch ein Abbruchtoken, um das Client-Signal zu ermöglichen, das sie getrennt ist, übergeben werden. Eine gute Vorgehensweise wäre das Token übergeben, wenn Sie eine der Rx aufrufen * Async-Methoden zur Unterstützung des Clients, die Verarbeitung zu beenden, ohne zu warten, bis alle Objekte verarbeitet werden können.
Die ForEachAsync wird keines Ergebnis, das bereits von einer Methode erste (oder "firstordefaultasync") verarbeitet, sodass Sie FirstOrDefaultAsync mit ForEachAsync verwenden können, um festzustellen, ob der Server nichts zu verarbeiten hat, bevor die Verarbeitung der nachfolgenden Objekte zu überprüfen, verarbeitet werden. Des Antragstellers IsEmpty-Methode wird die gleiche Überprüfung jedoch einfacher durchführen. Verfügt der Client für die Verarbeitungsergebnisse erforderlichen Ressourcen zuordnen, kann IsEmpty des Clients zu überprüfen, um festzustellen, ob etwas zu tun, bevor diese Ressourcen (eine Alternative wäre diese Ressourcen auf das erste Element in der Schleife verarbeitet) zuordnen. Ein Client, der überprüft, um festzustellen, ob die Ergebnisse vor dem Zuordnen von Ressourcen (und beginnend Verarbeitung) gleichzeitiger Unterstützung der Abbruch würde Code, der etwa so wie aussieht geben IsEmpty mit Abbildung 2.
Abbildung 2-Code zum Abbruch-Unterstützung und verzögern Verarbeitung bis Ergebnisse bereit sind
CancellationTokenSource cancelSource = new CancellationTokenSource();
CancellationToken cancel;
cancel = cancelSource.Token;
if (!await invMgmt.foundInvoice.IsEmpty())
{
// ...setup code for processing invoices...
try
{
invMgmt.foundInvoice.ForEachAsync(HandleInvoice, cancel);
}
catch (Exception ex)
{
if (ex.GetType() != typeof(CancellationToken))
{
// ...report message
}
}
// ...clean up code when all invoices are processed or client disconnects
}
Nachbereitung
Sie müssen ist eine einfache Implementierung des Observer-Musters ObservableCollection alle und führen Sie dann möglicherweise, die Sie verarbeiten ein Streams von Ergebnissen müssen. Um eine bessere Kontrolle und eine Ereignis-basierte Anwendung der Subject-Klasse und die Erweiterungen, die im Lieferumfang von Rx können Ihre Anwendung im asynchronen Modus arbeiten, indem eine leistungsfähige Implementierung des Veröffentlichen-Abonnieren-Modell unterstützt (und noch nicht besprochen, die umfangreiche Bibliothek von Operatoren, die im Lieferumfang von Rx). Wenn Sie mit Rx arbeiten, lohnt es sich, die Rx-Entwurfshandbuch herunterladen (bit.ly/1VOPxGS), die empfohlenen Vorgehensweisen im verarbeiten und erzeugen Observable Streams erläutert wird.
Rx bietet auch einige Unterstützung für die Konvertierung von den Nachrichtentyp, die zwischen dem Client und dem Server mithilfe der ISubject < TSource, TResult >-Schnittstelle übergeben. Die Schnittstelle ISubject < TSource, TResult > gibt zwei Datentypen: ein Datentyp 'in' und ein "Out"-Datentyp. Innerhalb der Subject-Klasse, die diese Schnittstelle implementiert, können Sie Vorgänge erforderlich, um das Ergebnis vom Server (der 'in'-Datentyp) zurückgegeben wird, in das Ergebnis vom Client (der "out"-Datentyp) erforderlich konvertieren ausführen. Darüber hinaus die in-Parameter ist kovariant (den angegebenen Datentyp akzeptiert oder nichts den Datentyp erbt) und der Out-Parameter ist kontravariant (akzeptiert den angegebenen Datentyp oder alle Elemente, die von ihr abgeleitet) bietet Ihnen zusätzliche Flexibilität.
Wir leben in einer Welt immer asynchron, und in einer solchen Welt des Observer-Musters geht wichtiger geworden – es ist ein nützliches Tool für jede Schnittstelle zwischen Prozessen, in dem der Serverprozess mehr als ein einzelnes Ergebnis gibt. Glücklicherweise haben Sie mehrere Optionen zum Implementieren des Observer-Musters in .NET Framework, einschließlich der ObservableCollection und Rx.
Peter Vogelist Systemarchitekten und PH & V Information Services. PH & V bietet vollem Stack Beratung von UX-Entwurf über Objektdesign Modellierung und -Datenbank.
Dank den folgenden technischen Experten von Microsoft für die Überprüfung dieses Artikels: Stephen Cleary, James McCaffrey und Dave Sexton
Stephen Cleary beschäftigt sich mit multithreading und asynchroner Programmierung für 16 Jahre und Async-Unterstützung in Microsoft .NET Framework seit der ersten Community Technology Preview verwendet. Er ist Autor von "Concurrency in c# Cookbook" (O' Reilly Media, 2014). Seine Homepage und seinen Blog befindet sich am stephencleary.com.