I followed Carlos Figueira's blog on client tracking in WCF self-hosted services and tried to put his client tracker to use in an IIS Hosted WCF service. I'm having some trouble with the extended class, the initialize event never fires for the IChannelInitializer object when the client is created.
The idea is to track client connections to a session based WCF service hosted in IIS, I particularly want to know when a client has disconnected so I can clean up some data that is streamed through a different service endpoint.
On the client I produced the binding by using the "Add Service Reference" in Visual Studio's solution explorer.
Carlos's blog: http://blogs.msdn.com/b/carlosfigueira/archive/2012/02/14/wcf-extensibility-initializers-instance-context-channel-call-context.aspx
WCF Service Implementation (Imlvc.cs)
[ServiceContract(SessionMode=SessionMode.Required)]
public interface Imlvc
{
[OperationContract]
int MyMethod();
[OperationContract]
int ConnectedClients();
}
WCF Service (mlvc.svc.cs)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class mlvc : Imlvc
{
static int m_Counter = 0;
public int MyMethod()
{
m_Counter++;
return m_Counter;
}
public int ConnectedClients()
{
int val = ClientTrackerChannelInitializer.ConnectedClientCount;
Debug.Write("ConnectedClients() called.");
return val;
}
}
public class ClientTrackerChannelInitializer : IChannelInitializer
{
internal static int ConnectedClientCount = 0;
public void Initialize(IClientChannel channel)
{
Debug.Write("Initialize was fired for ClientTrackerChannelInitializer");
ConnectedClientCount++;
channel.Closed += ClientDisconnected;
channel.Faulted += ClientDisconnected;
}
static void ClientDisconnected(object sender, EventArgs e)
{
Debug.Write("ClientDisconnected() was fired for ClientTrackerChannelInitializer");
ConnectedClientCount--;
}
}
public class ClientTrackerEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.ChannelDispatcher.ChannelInitializers.Add(new ClientTrackerChannelInitializer());
Debug.Write("endpointDispatcher added");
}
public void Validate(ServiceEndpoint endpoint) { Debug.Write("endpoint Validate Fired"); }
public override System.Type BehaviorType
{
get
{
Debug.Write("behavior property retrieved");
return typeof(ClientTrackerEndpointBehavior);
}
}
protected override object CreateBehavior()
{
Debug.Write("endpoint behavior created.");
return new ClientTrackerEndpointBehavior();
}
}
Web.config for the service
<?xml version="1.0"?>
<configuration>
<system.web>
<customErrors mode="Off" />
<compilation debug="false"/>
</system.web>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="myClientTracker"
type="MasterListWCF.ClientTrackerEndpointBehavior, MasterListWCF" />
</behaviorExtensions>
</extensions>
<services>
<service behaviorConfiguration="sessionBehavior" name="MasterListWCF.mlvc">
<endpoint address="" behaviorConfiguration="myClientTracker"
binding="customBinding" bindingConfiguration="sessionHttpsBindingConfiguration"
name="sessionHttpEndpoint" contract="MasterListWCF.Imlvc" />
<endpoint address="mex" binding="mexHttpBinding" name="mexHttpSessionEndpoint"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://m.managedsp.com/testsvc/mlvc.svc" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="myClientTracker" />
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="sessionBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="500" maxConcurrentSessions="500"
maxConcurrentInstances="500" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="sessionHttpsBindingConfiguration">
<reliableSession/>
<httpsTransport/>
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Console application that creates the client and calls service methods
static void Main(string[] args)
{
ImlvcClient client = new ImlvcClient();
Console.WriteLine(client.State);
Console.WriteLine("Connected clients " + client.ConnectedClients());
Console.WriteLine("My method " + client.MyMethod());
Console.WriteLine("My method " + client.MyMethod());
Console.WriteLine("My method " + client.MyMethod());
Console.WriteLine(client.State);
Console.ReadKey();
}
app.config for the client
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="myClientTracker"
type="MasterListWCF.ClientTrackerEndpointBehavior, MasterListWCF" />
</behaviorExtensions>
</extensions>
<client>
<endpoint address="https://m.managedsp.com/testsvc/mlvc.svc" behaviorConfiguration="myClientTracker"
binding="customBinding" bindingConfiguration="sessionHttpsBindingConfiguration"
contract="mlvc.Imlvc" name="sessionHttpEndpoint" />
</client>
<bindings>
<customBinding>
<binding name="sessionHttpsBindingConfiguration">
<reliableSession/>
<httpsTransport/>
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="myClientTracker" />
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Here is the output from the console app
Created
Connected clients 0
My method 1
My method 2
My method 3
Opened
The problem is the initialize event is never fired when the client is created. Here is the debug log for the WCF process. You can see that ClientConnections is called but the initialize method is never called for the IChannelInitializer.
'w3wp.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\testsvc\d85651f1\367baff8\assembly\dl3\edc64503\46551bf1_57d3cf01\MasterListWCF.dll', Symbols loaded.
'w3wp.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.Services.Design\v4.0_4.0.0.0__b77a5c561934e089\System.Data.Services.Design.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
'w3wp.exe' (Managed (v4.0.30319)): Loaded 'Microsoft.GeneratedCode'
ConnectedClients() called.
The thread '<No Name>' (0x2f0) has exited with code 0 (0x0).
Can anyone point me in the right direction as to why the Initialize method is not fired?