2012-07-06 15 views
5

Von einer Client-Anwendung sende ich eine Anfrage/Transaktion (enthält eine Operation (und Parameter) + transactionID) zu einer fernen Warteschlange. Der Remote-Server entfernt die Anfrage zu einem bestimmten Zeitpunkt und benötigt etwas Zeit, um sie zu verarbeiten.Benötigen Sie ein besseres Design für eine "Callback Dispatcher" -Komponente

Sobald es fertig ist, verarbeitet es, sendet es eine Antwort auf die Client-Warteschlange (enthält die applicative Antwort + die transactionID) ... so ist dies eine völlig "getrennte" Kommunikationsmodus, die einzige Möglichkeit, die der Client zuordnen kann Die Antwort auf die Anfrage erfolgt über die Transaktions-ID.

Die Nachrichtenantwort wird auf der Clientseite entfernt und mit der ursprünglichen Anfrage abgeglichen (basierend auf der transactionID).

Was ich gerade mache ist, dass, wenn der Client die Anfrage an die Server-Warteschlange posten, fügt er einen Rückruf zu einem Wörterbuch hinzufügen TransaktionsID und Callback (Delegat). Dies ist eine Dictionary<int, object> Zuordnung einer transactionId zurück zu einem Rückruf mit dem Ergebnis der Operation aufrufen.

die Rückrufe/Teilnehmer als Objekt gespeichert sind, aufgrund der Tatsache, dass in Abhängigkeit von der Anforderung, die Rückruf delegieren Signatur unterscheidet (beispielsweise eine Antwort kann eine List<string> zurückzukehren, während eine andere Reaktion eine int zurückkehren kann).

Wenn die Clientwarteschlange eine Antwort aus der Warteschlange nimmt, kennt sie den Typ der Antwort (und damit die entsprechende Signatur des Rückrufs). Daher ruft sie den Rückruf aus dem Wörterbuch ab, basierend auf der transactionID. Anschließend wird das Objekt an den entsprechenden Delegattyp zurückgegeben und der Rückruf aufgerufen.

Ich finde diesen Ansatz nicht sehr "sexy", aber ich sehe keine andere Möglichkeit, eine solche Aufgabe zu erfüllen.

Gibt es eine bessere Möglichkeit, dies durchzuführen?

Wenn das Problem nicht klar genug ist, lass es mich wissen und werde mit einigen Änderungen klären.

+0

mögliches Duplikat von [C#: Verwendet Random und OrderBy einen guten Shuffle-Algorithmus?] (Http://stackoverflow.com/questions/1287567/c-is-using-random-and-orderby-a-good-shuffle -Algorithmus) – Arion

Antwort

3

Sie könnten Reactive Extensions ein praktisches Tool für diese Art von Messaging finden.

Als Erstes machen Sie alle Ihre Antworten implementieren einige Schnittstelle oder Basisklasse, wie IMessage. Jede Art von Antwort sollte wie hier

public interface IMessage 
{ 
    int TransactionId { get; } 
} 

public class UserListMessage : IMessage 
{ 
    int TransactionId { get; set; } 
    public List<string> Users { get; set; } 
} 

dann Ihre Nachrichtenwarteschlange IObservable<IMessage> implementieren machen in einer separaten Klasse verkapselt werden. Reaktive Erweiterungen bieten eine sofort verwendbare Implementierung namens Subject<T>, die Sie vielleicht umschließen möchten.

Observable implementiert eine Subscribe(IObserver<IMessage> observer) Methode, die Beobachter irgendwo in einer internen Liste speichert. Wenn eine neue Antwort eintrifft, wird eine OnNext(IMessage message) Methode für jeden abonnierten Beobachter aufgerufen.

Schließlich Ihr Response-Handling-Registrierungscode könnte wie folgt aussehen:

var subscription = myQueue 
    .OfType<UserListMessage>() 
    .Where(msg => msg.TransactionId == id) 
    .Subscribe(callback); 

Das Rückruf für eine Nachricht vom Typ UserListMessage mit bestimmten Transaktions-ID registriert. Wahrscheinlich werden Sie sich auch irgendwo abmelden wollen. Das wird sein:

subscription.Dispose(); 

Das war ein kurzes Beispiel, wie das mit Rx aussehen würde. Jetzt geh und finde ein ausführlicheres Tutorial.

1

Sie können auf der Clientseite eine Aufzählung erstellen, die alle möglichen Antworten definiert, die Sie erhalten können. Dann können Sie eine Funktion codieren, die einen großen "Select Case" enthält, der jeden Wert der Enumeration seinem spezifischen Funktionsaufruf zuordnet. Dann können Sie Ihr Wörterbuch die Transaktions-ID mit dem Wert der Aufzählung verknüpfen, die die Art der Antwort, die Sie vom Server erhalten, identifizieren.

Abhängig von der Art der Funktionalität, die Sie für jede Art von Antwort implementieren müssen, können Sie vielleicht eine "objektorientierte" Art der Dinge zu tun finden ... Vielleicht können Sie eine Basisklasse ResponseAction erstellen, mit einem Common Antwort-Methode, und dann können Sie von dieser Klasse für jede der möglichen Antworttypen vererben, die Sie erhalten können, und dann, wenn Sie den Server aufrufen, instanziieren Sie die richtige Response_Specific_Action-Klasse, und diese Instanz in das Wörterbuch einfügen ... und dann Für jede Antwort verwenden Sie Ihr Wörterbuch, um die richtige Instanz zu finden, und rufen dieselbe Standard-Respond-Methode auf, die die spezifische Implementierung von Response_Specific_Action verwendet.

Wenn Sie sich für die ResponseAction-Klassenroute entscheiden, können Sie auch die transactionID als eine Eigenschaft der Basisklasse einbeziehen.

1

Sie Situation ist irgendwie nicht sehr klar für mich. Zuerst haben Sie nicht erwähnt, welchen Kommunikationsmechanismus Sie während der Client-Server-Transaktion verwenden. benutzt du WCF und seine Callback-Kanäle?

Warum wickeln Sie nicht alle Anfragenantwortnachrichten unter die Basisklasse, die einige gemeinsame Daten für alle Transaktionen enthält?

U traurig, dass Sie List für Ihr Wörterbuch verwenden, da die Antwort von Transaktion zu Transaktion unterschiedlich ist, aber wieder warum Objekt? Ist es nicht besser, einen gemeinsamen Vertrag für alle Antwortnachrichten zu haben? Bitte teilen Sie uns ein wenig mehr über den Kommunikationsfluss zwischen Ihrem Client und Server mit.

1

Das klingt genau wie ein Adaptermusterproblem. Einen guten Überblick über den Adapter finden Sie unter this link.

Was Sie tun müssen, ist eine Abstraktion erstellen, die die gesamte Client-Antwort einkapselt (einschließlich was auch immer Sie tun, mit diesem List<string> oder int). Ihre Abstrahierung muss breit genug sein, damit Ihre unterschiedlichen Verhaltensweisen von derselben Signatur abgedeckt werden können. Etwas wie folgt aus:

public abstract class MessageCompleteHandler 
{ 
    public abstract void Execute(); 
} 

//name this one better, just an example :) 
public class ListOfStringHandler : MessageCompleteHandler 
{ 
    public override void Execute() 
    { 
     //get list of strings 
     // do something with it 
    } 
} 

public class MessageCompleteHandlerFactory 
{ 
    public MessageCompleteHandler GetHandler(int transactionId) 
    { 
     //this replaces/uses your dictionary to match handlers with types. 
    } 
} 

Dann, wenn Sie die Antwort erhalten, verwenden Sie die Transaktions-ID die entsprechende Handler-Objekt zu erstellen, und auszuführen. Dies funktioniert am besten, wenn die Anzahl der Handler-Varianten natürlich relativ klein ist.

+0

Schön, aber wo ist der Adapter hier? – Pein

+0

Die MessageCompleteHandler-Implementierungen sind Adapter für die verschiedenen Delegaten. Anpassen der verschiedenen Signaturen an eine einzige Schnittstelle. – tallseth

+0

Ihr Beispiel enthält keine Delegierten. Ich denke, würde nicht wissen, wie Sie Ihre Lösung implementieren, wenn ich wollte. Momentan sieht es eher wie ein Befehlsmuster aus, nicht Adapter. – Pein