I'm building a service into an existing application where each service was built with the intention that it would only be consumed by one client and the client and server are setup with duplex communication channels.
Limitations
- I don't have the option of re-designing that existing infrastructure
- I cannot use a shareable session
Requirement:
- I need to be able to communicate between client services (For instance, if a user clicks on an item and wants to share that item, the client, for whatever reason, might not be able to handle the "sharing" feature and needs to pass it on to another client to handle - this must be done through the service)
- Communication between clients must be conducted by the service.
To get this working initially, I setup an IPC channel (netNamedPipeBinding) directly between the two clients, but I was told to send everything through the server. The "server" in this scenario, in most cases, is running on the same machine as the client so I came up with this very crude proof of concept attempt (see below code block).
Issue: When a method is invoked for a subscribing service, the operation context for the current service (within which the method is being invoked) is null - this leaves the service without any way to call back to the client
I was considering using Juval Löwy's publish/subscribe framework that he provides in his ServiceModelEx framework, but it seemed unnecessary when all of the clients already have duplex communication setup between themselves and their respective services... so the intent is just to add a simple publishing/subscribing layer that sits conceptually "underneath" those services and can speak to any of them that care to subscribe.
Advice and constructive criticism is welcomed!
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;
}
}
So first off, it seems the pattern I was implementing could be classified as a Mediator Pattern.
So I solved this by passing in the current instance context of the FireEvent calling service which, in my case, should be the same context as the subscribing service.
The case I am handling here is disconnected client applications that are operating in the context of the same user and from the same client machine, but (per requirements) they must communicate through the service layer.
I started going down the road of using the WCF synchronization context and Juval Lowy's
ThreadPoolBehavior
class from hisServiceModelEx
library, but I am tied to an implementation where Ninject WCF Extensions were getting in the way of this.So this solution will have to be adapted based on your own implementation, but to give you an idea of how I got this to work, here is the gist of my updated
FireEvent
method: