2

各サービスが 1 つのクライアントによってのみ消費され、クライアントとサーバーが二重通信チャネルでセットアップされることを意図して構築された既存のアプリケーションにサービスを構築しています。

制限事項

  1. その既存のインフラストラクチャを再設計するオプションはありません
  2. 共有可能なセッションを使用できません

要件:

  1. クライアント サービス間で通信できるようにする必要があります (たとえば、ユーザーがアイテムをクリックしてそのアイテムを共有したい場合、クライアントは何らかの理由で「共有」機能を処理できない可能性があり、渡す必要があります。処理するために別のクライアントに送信します - これはサービスを介して行う必要があります)
  2. クライアント間の通信は、サービスによって行われる必要があります。

これを最初に機能させるために、2 つのクライアント間で IPC チャネル (netNamedPipeBinding) を直接セットアップしましたが、すべてをサーバー経由で送信するように言われました。このシナリオの「サーバー」は、ほとんどの場合、クライアントと同じマシンで実行されているため、この非常に大雑把な概念実証の試みを思い付きました (以下のコード ブロックを参照)。

問題: サブスクライブしているサービスに対してメソッドが呼び出されると、(メソッドが呼び出されている) 現在のサービスの操作コンテキストが null になります。これにより、クライアントにコールバックする方法がなく、サービスが終了します。

私は、Juval Löwy が ServiceModelEx フレームワークで提供しているパブリッシュ/サブスクライブ フレームワークを使用することを検討していましたが、すべてのクライアントが自分自身とそれぞれのサービスとの間で二重通信のセットアップを既に行っている場合は不要に思われました。概念的にこれらのサービスの「下」に位置し、サブスクライブしたいサービスと通信できるパブリッシング/サブスクライブ層。

アドバイスと建設的な批判を歓迎します!


    public static class SubscriptionManager<TEventArgs> 
    where TEventArgs : class
{
    private static ConcurrentDictionary<int, Action<TEventArgs>> _subscribers =
       new ConcurrentDictionary<int, Action<TEventArgs>>();

    // sessionId is NOT the WCF SessionId
    public static void FireEvent( int sessionId, TEventArgs eventArgs ) 
    {
        var subscribers = _subscribers.Where( s => s.Key == sessionId );
        var actions = subscribers.Select( keyValuePair => keyValuePair.Value );

        foreach ( var action in actions )
            action.BeginInvoke( eventArgs, action.EndInvoke, null );
    }

    public static void Subscribe(int sessionId, Action<TEventArgs> eventHandler)
    {
        _subscribers.TryAdd(sessionId, eventHandler);
    }

    public static Action<TEventArgs> Unsubscribe(int sessionId)
    {
        Action<TEventArgs> eventHandler;
        _subscribers.TryRemove(sessionId, out eventHandler);
        return eventHandler;
    }
}
4

1 に答える 1

1

まず、私が実装していたパターンはMediator Patternとして分類できるようです。

そこで、FireEvent 呼び出しサービスの現在のインスタンス コンテキストを渡すことでこれを解決しました。私の場合は、サブスクライブ サービスと同じコンテキストにする必要があります。

ここで扱っているケースは、同じユーザーのコンテキストで同じクライアント マシンから動作している切断されたクライアント アプリケーションですが、(要件により) サービス層を介して通信する必要があります。

私は WCF 同期コンテキストと Juval Lowy のライブラリThreadPoolBehaviorからのクラスを使用する道を歩み始めServiceModelExましたが、Ninject WCF 拡張機能がこれを妨げていた実装に縛られています。

したがって、このソリューションは独自の実装に基づいて調整する必要がありますが、これをどのように機能させたかを理解するために、更新されたFireEventメソッドの要点を次に示します。

public static void FireEvent( int sessionId, TData eventData, InstanceContext context )
{
    var subscribers = Subscribers.Where( s => s.Key == sessionId );

    var eventArguments = subscribers.Select( kvp => kvp.Value );

    foreach ( var argument in eventArguments )
    {
        // set data associated with the event
        argument.SetData( eventData );

        NinjectInstanceProvider instanceProvider = null;
        Object instance = null;

        try
        {
            // get a "synchronized instance" of the service of type defined by event args
            instanceProvider = new NinjectInstanceProvider( argument.ServiceType );
            instance = instanceProvider.GetInstance( context );

            // get the method from our "synchronized instance"
            // filter by argument types so we don't run into any issues with ambiguity
            var argumentTypes = new[] { typeof ( TEventArgs ) };
            var method = instance.GetType().GetMethod( argument.Callback, argumentTypes );

            // our method arguments
            var arguments = new object[] { argument };

            // invoke the method on our "synchronized instance"
            method.Invoke( instance, arguments );

            // release the instance
            instanceProvider.ReleaseInstance( context, instance );
        }
        catch
        {
            // log
        }
        finally
        {
            if( provider != null )
            {
                if( instance != null ) { instanceProvider.ReleaseInstance( context, instance ); }
            }
        }
    }
}
于 2013-05-15T17:56:40.723 に答える