5

クライアントアプリケーションから、リクエスト/トランザクション(実行する操作(およびパラメーター)+transactionIDを含む)をリモートキューに送信しています。リモートサーバーは、ある時点で要求をデキューし、処理に時間がかかります。

処理が完了すると、クライアントキューに応答を送信します(適用可能な応答とtransactionIDを含む)...したがって、これは完全に「切断された」通信モードであり、クライアントが応答をにマップできる唯一の方法です。リクエストはtransactionIDを介して行われます。

メッセージ応答はクライアント側でデキューされ、元の要求と一致します(transactionIDに基づく)。

私が今していることは、クライアントがサーバーキューにリクエストを投稿すると、transactionIdとコールバック(デリゲート)を保持する辞書にコールバックを追加することです。これは、Dictionary<int, object>transactionIdをコールバックにマッピングして戻し、操作の結果を呼び出します。

コールバック/デリゲートは、リクエストに応じてコールバックデリゲートのシグネチャが異なるため、オブジェクトとして保存されます(たとえば、応答がを返す場合とList<string>、別の応答がを返す場合がありますint)。

クライアントキューは、応答をデキューするときに、応答のタイプ(したがって、対応するコールバックのシグネチャ)を認識しているため、transactionIDに基づいてディクショナリからコールバックを取得します。次に、オブジェクトを対応するデリゲートタイプにキャストして戻し、コールバックを呼び出します。

このアプローチはあまり「セクシー」ではないと思いますが、そのようなタスクを実行する別の方法は実際にはわかりません。

これを実行するためのより良い方法はありますか?

問題が十分に明確でない場合は、私に知らせてください。いくつかの編集で明らかになります。

4

4 に答える 4

3

Reactive Extensions は、この種のメッセージングに便利なツールであることに気付くかもしれません。

まず、すべての応答に、IMessage などのインターフェイスまたは基本クラスを実装させます。各種類の応答は、ここのように個別のクラスにカプセル化する必要があります

public interface IMessage
{
    int TransactionId { get; }
}

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

次に、メッセージ キューを実装しIObservable<IMessage>ます。Reactive extensions は と呼ばれるすぐに使用できる実装を提供しますSubject<T>

Observable は、Subscribe(IObserver<IMessage> observer)オブザーバーを内部リストのどこかに格納するメソッドを実装します。新しい応答が到着するOnNext(IMessage message)と、サブスクライブされた各オブザーバーでメソッドが呼び出されます。

最後に、応答処理の登録コードは次のようになります。

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

これにより、指定されたトランザクション ID を持つ UserListMessage タイプのメッセージのコールバックが登録されます。おそらく、あなたもどこかで購読を解除したいと思うでしょう。それは次のようになります。

subscription.Dispose();

これは、Rx でどのように見えるかの簡単なサンプルでした。次に、より詳細なチュートリアルを見つけてください。

于 2012-07-06T22:49:26.090 に答える
1

クライアント側で、取得可能なすべてのタイプの応答を定義する列挙を作成できます。次に、列挙の各値を特定の関数呼び出しに関連付ける大きな「選択ケース」を含む関数をコーディングできます。次に、辞書を作成して、サーバーから取得する応答の種類を識別する列挙型の値に transactionID を関連付けることができます。

応答の各タイプに実装する必要がある機能の種類に応じて、より「オブジェクト指向」の方法を見つけることができるかもしれません...共通のRespondメソッドを使用して、基本クラスのResponseActionを作成し、次に、取得できる可能な応答タイプごとにこのクラスから継承できます。次に、サーバーを呼び出すときに、適切な Response_Specific_Action クラスをインスタンス化し、このインスタンスを辞書に入れます...そして、各応答で辞書を使用して適切なインスタンスを見つけ、同じ標準の Respond メソッドを呼び出すと、その Response_Specific_Action の「固有の」実装が使用されます。

ResponseAction クラス ルートを使用する場合は、トランザクション ID を基本クラスのプロパティとして含めることも検討してください。

于 2012-07-06T22:28:23.983 に答える
1

あなたの状況は、どういうわけか私にはあまり明確ではありません。まず第一に、クライアントサーバートランザクション中にどの通信メカニズムを使用しているかについて言及していませんか? WCF とそのコールバック チャネルを使用していますか?

すべてのトランザクションに共通のデータを持つ基本クラスの下で、すべての要求応答メッセージをラップしないのはなぜですか?

トランザクションごとに応答が異なるため、辞書にリストを使用しているのは残念ですが、なぜ反対するのですか? すべての応答メッセージに対して共通の契約を結ぶ方が難しいのではないでしょうか? クライアントとサーバー間の通信の流れについてもう少し教えてください

于 2012-07-06T22:37:32.170 に答える
1

これは、Adapter パターンの問題とまったく同じように聞こえます。アダプターの概要については、このリンクを参照してください。

あなたがする必要があるのは、クライアントの応答全体をカプセル化する抽象化を作成することです (それを使って何をするかを含むList<string>) int。さまざまな動作をすべて同じシグネチャでカバーできるように、抽象化の範囲は十分に広くする必要があります。このようなもの:

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.
    }
}

次に、応答を取得したら、トランザクション ID を使用して適切なハンドラー オブジェクトを作成し、それを実行します。もちろん、これは、ハンドラー バリアントの数がかなり少ない場合に最適に機能します。

于 2012-07-06T23:23:22.550 に答える