1

Consider the following:

I have one master server and several servers on different locations which can communicate with the master server. Additionally (GUI) clients can connect to each server.

So I have a public interface which is known by everyone:

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IBaseService
{
    [OperationContract]
    void Subscribe();

    [OperationContract]
    void Unsubscribe();
}

Additionally the servers support a callback contract so that the master server can trigger requests on the servers connected to it.

[ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
interface IServerService : IBaseService
{
}

public interface ICallback
{
    [OperationContract]
    [FaultContract(typeof(ExceptionDetail))]
    void TheCallback();
}

Meaning the server has 2 implemented contracts. So when I iterate all subscribers (on Subscribe I cache all OperationContext) on the master server and call the callback contract for clients it just times out! I had expected some kind of ContractMismatch. Anything (and immediately!) but an operation timeout?!?

I also tried to figure out the contract name the client used to connect (OperationContext.Current). But that was also not successful. It says IServerService in both cases in OperationContext.Current.EndpointDispatcher.ContractName.

Is there any possibilty on server side to figure out which OperationContract was used by the client when connecting?

Of course I could extend the server interface by a method SubscribeServer but that seems rather ugly to me.

EDIT:

as suggested I did implement a MessageInspector. But in the AfterReceiveRequest the IClientChannel can be successfully casted to ICallback

Whats even more weird is that the call stack contains already ReliableDuplexSessionChannel:

>   XYZ.exe!XYZ.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) Line 99  C#
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.AfterReceiveRequestCore(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x86 bytes 
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref System.ServiceModel.Dispatcher.MessageRpc rpc) + 0x37 bytes 
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool isOperationContextSet) + 0x151 bytes 
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext request, bool cleanThread, System.ServiceModel.OperationContext currentOperationContext) + 0x644 bytes 
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext request, System.ServiceModel.OperationContext currentOperationContext) + 0x1d2 bytes    
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult result) + 0x4b bytes 
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes  
System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0x16b bytes 
System.Runtime.DurableInstancing.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.AsyncQueueReader.Set(System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.Item item) + 0x41 bytes   
System.Runtime.DurableInstancing.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.Dispatch() + 0x320 bytes   
System.ServiceModel.dll!System.ServiceModel.Channels.ReliableDuplexSessionChannel.ProcessDuplexMessage(System.ServiceModel.Channels.WsrmMessageInfo info) + 0x7cb bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.ServerReliableDuplexSessionChannel.ProcessMessage(System.ServiceModel.Channels.WsrmMessageInfo info) + 0x2a7 bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.ReliableDuplexSessionChannel.HandleReceiveComplete(System.IAsyncResult result) + 0x1fa bytes   
System.ServiceModel.dll!System.ServiceModel.Channels.ReliableDuplexSessionChannel.OnReceiveCompletedStatic(System.IAsyncResult result) + 0x86 bytes 
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes  
System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0x16b bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.ReliableChannelBinder<System.__Canon>.InputAsyncResult<System.__Canon>.OnInputComplete(System.IAsyncResult result) + 0x7a bytes    
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes  
System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0x16b bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.FramingDuplexSessionChannel.TryReceiveAsyncResult.OnReceive(System.IAsyncResult result) + 0xa9 bytes   
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes  
System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0x16b bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.SynchronizedMessageSource.ReceiveAsyncResult.OnReceiveComplete(object state) + 0x82 bytes  
System.ServiceModel.dll!System.ServiceModel.Channels.SessionConnectionReader.OnAsyncReadComplete(object state) + 0x175 bytes    
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result) + 0x32 bytes  
System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken) + 0xc5 bytes    
System.dll!System.Net.Security.NegotiateStream.ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count, System.Net.AsyncProtocolRequest asyncRequest) + 0x126 bytes    
System.dll!System.Net.Security.NegotiateStream.ReadCallback(System.Net.AsyncProtocolRequest asyncRequest) + 0xea bytes  
System.dll!System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(int bytes) + 0x32 bytes 
System.dll!System.Net.FixedSizeReader.ReadCallback(System.IAsyncResult transportResult) + 0x9c bytes    
System.Runtime.DurableInstancing.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously) + 0x16b bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.ConnectionStream.ReadAsyncResult.OnAsyncReadComplete(object state) + 0xa2 bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.SocketConnection.AsyncReadCallback(bool haveResult, int error, int bytesRead) + 0x19b bytes    
System.Runtime.DurableInstancing.dll!System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(uint error, uint bytesRead, System.Threading.NativeOverlapped* nativeOverlapped) + 0x40 bytes  
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x96 bytes    
[Native to Managed Transition]  

Does this mean if my service class implements the IServerService which has a callback contract defined, that all connecting Clients are routed via a duplex proxy? No matter the contract used (or implemented) by the client?

toATwork
  • 1,335
  • 16
  • 34

2 Answers2

0

In server side, another object that contains incomming message data is OperationContext.Current.Host.

You can get information about the contracts. Here is some data available:

Interface name: OperationContext.Current.Host.ImplementedContracts.FirstOrDefault().Value.Name

Interface namespace : OperationContext.Current.Host.ImplementedContracts.FirstOrDefault().Value.Namespace

Full contract type name: OperationContext.Current.Host.ImplementedContracts.FirstOrDefault().Value.ContractType.FullName

You can also use Reflection in OperationContext.Current.Host.Description.ServiceType, that is a type.

Hope this helps.

Ricardo Pontual
  • 3,749
  • 3
  • 28
  • 43
  • The `OperationContext.Current.Host` contains information about the ServiceHost. I am interested in the information about the client. The contract the client used for channel creation. In the ImplmentedContracts on the ServiceHost I see the my IServerService and my IClientService. But no indication which contract was used by the client. – toATwork Dec 06 '16 at 13:03
  • Maybe you'll need to implement a Message Inspector. – Ricardo Pontual Dec 06 '16 at 13:17
  • No, that did no good. Please see additional information in the question. – toATwork Dec 06 '16 at 14:04
  • Interesting, I have a routing service with message inspector here, but it's not using call back, it's a simple routing based in header, I will try to replicate your scenario here. – Ricardo Pontual Dec 06 '16 at 14:10
0

If there are 2 contracts, one is duplex and one isn't, the Channel type can be checked to see if it is the callback interface type or not:

' we want to support optional callbacks, so this just returns nothing if the callback isn't what we are looking for
If TypeOf OperationContext.Current?.Channel Is TDuplexClientCallback Then
    Return OperationContext.Current?.GetCallbackChannel(Of TDuplexClientCallback)
Else
    Return Nothing
End If
Mafu Josh
  • 2,523
  • 1
  • 23
  • 25