2

我正在将服务构建到现有应用程序中,其中构建每个服务的目的是仅由一个客户端使用,并且客户端和服务器设置有双工通信通道。

限制

  1. 我没有重新设计现有基础设施的选项
  2. 我无法使用可共享会话

要求:

  1. 我需要能够在客户端服务之间进行通信(例如,如果用户单击一个项目并想要共享该项目,客户端出于某种原因可能无法处理“共享”功能并需要通过它交给另一个客户处理 - 这必须通过服务完成)
  2. 客户端之间的通信必须由服务进行。

为了让这个最初工作,我直接在两个客户端之间设置了一个 IPC 通道(netNamedPipeBinding),但我被告知通过服务器发送所有内容。在大多数情况下,这种情况下的“服务器”与客户端在同一台机器上运行,所以我想出了这个非常粗略的概念验证尝试(见下面的代码块)。

问题:当为订阅服务调用方法时,当前服务(在其中调用方法)的操作上下文为空 - 这使服务无法回调客户端

我正在考虑使用 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 的ThreadPoolBehaviorServiceModelEx,但我与 Ninject WCF Extensions 阻碍了这一点的实现相关联。

所以这个解决方案必须根据你自己的实现进行调整,但是为了让你了解我是如何让它工作的,这里是我更新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 回答