Extending the WCF Stack

In my last post I created a custom serializer that could be injected into the WCF plumbing between the dispatcher and the service class that allowed us to debug errors that occurred during serialization. This operation behaviour could be applied to a method signature in the service contract :
 
[ServiceContract()]
public interface IMyService
{
	[OperationContract]
	[DebuggableSerializer]
	Thing GetThing(Flavour flavour); 
	
	[OperationContract]
	[DebuggableSerializer]
	Thing[] GetOneOfEach();
}
or to the implementation of that method in the service class :
 
[DebuggableSerializer]
public Thing[] GetOneOfEach()
{
	List<Thing> things = new List<Thing>();
	foreach(Flavour f in Enum.GetValues(typeof(Flavour)))
	{
		things.Add(this.GetThing(f));
	}
	return things.ToArray();
}
However, what if I wanted to apply this behaviour as a ServiceBehavior, ContractBehavior or EndpointBehavior rather than an OperationBehavior i.e. if I wanted all the operations in my service implementation or interface to use it and I don’t want to have to go through and add the attribute to every method ? How can I use my [DebuggableSerializer] attribute to decorate a service class, interface or even better, apply the behaviour to the service or endpoint using a config file so I don’t have to change my code at all ?
 
So, in this post I am going to extend my [DebuggableSerializer] attribute so it can be used on a service class or interface and I am going to extend the System.ServiceModel.Configuration.BehaviorExtensionElement to allow me to control and apply that behaviour from my config file to either a service or a specific endpoint.
 
My attribute in the previous example looked like this :
 
public class DebuggableSerializer : Attribute, IOperationBehavior
but now we want to use it as a ServiceBehavior, ContractBehavior and an EndpointBehavior as well as an OperationBehavior, so we’ll implement the IServiceBehavior, IContractBehavior and IEndpointBeahvior  interfaces :
 
public class DebuggableSerializer : Attribute, IOperationBehavior, 
	IServiceBehavior, IEndpointBehavior, IContractBehavior
IServiceBehavior has three methods for which we need to add implementations and IContractBehavior and IEndpointBehavior have 4 :
 
#region IServiceBehavior Members 

public void AddBindingParameters(ServiceDescription serviceDescription,
	ServiceHostBase serviceHostBase, 
	System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
	BindingParameterCollection bindingParameters)
{
	ReplaceDataContractSerializerOperationBehavior(serviceDescription);
}

public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
	ServiceHostBase serviceHostBase)
{
	ReplaceDataContractSerializerOperationBehavior(serviceDescription);
} 

public void Validate(ServiceDescription serviceDescription,
	ServiceHostBase serviceHostBase)
{
} 

#endregion

#region IEndpointBehavior Members 

public void AddBindingParameters(ServiceEndpoint endpoint,
	BindingParameterCollection bindingParameters)
{
} 

public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
	ReplaceDataContractSerializerOperationBehavior(endpoint);
} 

public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
	EndpointDispatcher endpointDispatcher)
{
	ReplaceDataContractSerializerOperationBehavior(endpoint);
} 

public void Validate(ServiceEndpoint endpoint)
{
}

#endregion

#region IContractBehavior Members 

public void AddBindingParameters(ContractDescription contractDescription,
	ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{ 
} 

public void ApplyClientBehavior(ContractDescription contractDescription,
	ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
	ReplaceDataContractSerializerOperationBehavior(contractDescription);
} 

public void ApplyDispatchBehavior(ContractDescription contractDescription,
	ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
	ReplaceDataContractSerializerOperationBehavior(contractDescription);
} 

public void Validate(ContractDescription contractDescription,
	ServiceEndpoint endpoint)
{ 
} 

#endregion
We also need to provide 3 overloads of the ReplaceDataContractSerializerOperationBehavior method, one that takes ServiceDescription, one that takes ServiceEndpoint and one that takes ContractDescription instead of OperationDescription. The method that takes ServiceDescription will call the method that takes ServiceEndpoint for each Endpoint and the method that takes ServiceEndpoint will call the method that takes ContractDescription and the method that takes ContractDescription will call the method that takes OperationDescription for each OperationDescription in its contract.
 
private static void ReplaceDataContractSerializerOperationBehavior(
	ServiceDescription description)
{
	foreach (ServiceEndpoint endpoint in description.Endpoints)
	{
		ReplaceDataContractSerializerOperationBehavior(endpoint);
	}
}

private static void ReplaceDataContractSerializerOperationBehavior(
	ContractDescription description)
{
	foreach (OperationDescription desc in description.Operations)
	{
		ReplaceDataContractSerializerOperationBehavior(desc);
	}
} 

private static void ReplaceDataContractSerializerOperationBehavior(
	ServiceEndpoint endpoint)
{
	// ignore mex
	if (endpoint.Contract.ContractType == typeof(IMetadataExchange))
	{
		return;
	}
	ReplaceDataContractSerializerOperationBehavior(endpoint.Contract);
}
So now we can take our DebuggableSerializer attribute and place it on the service class or interface :
 
[DebuggableSerializer]
public class MyService : IMyService
{ ... }

[DebuggableSerializer]
public interface IMyService
{ ... }
We now have an attribute that can be used to replace the serializer on a service contract or an operation contract or a service operation or on a whole service class or an endpoint of a service. The final step is to be able to add this behaviour to a service or an endpoint from within our configuration rather than in code. For this we need to create a BehaviorExtensionElement that will create an instance of our DebuggableSerializer behaviour. This is very simple :
 
public class UseDebuggableSerializer : BehaviorExtensionElement
{ 
	public override Type BehaviorType
	{
		get { return typeof(DebuggableSerializer); }
	}
	
	protected override object CreateBehavior()
	{
		return new DebuggableSerializer();
	}
}
Next, in our config file, we need to define this behaviour extension :
 
<system.serviceModel>
	<extensions>
		<behaviorExtensions>
			<add name="UseDebuggableSerializer"
				type="WCFLibrary.UseDebuggableSerializer, WCFLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
		</behaviorExtensions>
	</extensions>
	<!-- elided -->
</system.serviceModel>
To add this behaviour to a service we add the extension to the service’s assigned behaviour :
 
<system.serviceModel>
	<!-- elided -->
	<behaviors>
		<serviceBehaviors>
			<behavior name="MetadataExchange">
				<serviceMetadata httpGetEnabled="true" />
				<UseDebuggableSerializer />
			</behavior>
		</serviceBehaviors>
	</behaviors>
	<!-- elided -->
</system.serviceModel>
To add it to an endpoint we define an endpoint behaviour and add the extension to that :
 
<system.serviceModel>
	<!-- elided -->
	<behaviors>
		<endpointBehaviors>
			<behavior name="DebuggableSerializer">
				<UseDebuggableSerializer />
			</behavior>
		</endpointBehaviors>
	</behaviors>
	<!-- elided -->
	<service behaviorConfiguration="MetadataExchange" 
		name="WCFLibrary.MyService">
	<endpoint
		address="net.tcp://localhost:7000/MyService"
		behaviorConfiguration="DebuggableSerializer"
		binding="netTcpBinding"
		name="TCP_7000_MyService"
		contract="WCFLibrary.IMyService" />
	</service>
	<!-- elided -->
</system.serviceModel>

Advertisements

Debugging the WCF Stack

As if starting developing with WCF wasn’t a hard enough paradigm shift what with getting used to sharing schema instead of type and being cruelly abstracted away from all our lovely plumbing, the abstraction itself it can throw some interesting curve-balls. It’s all very well taking the plumbing and abstracting it away but what happens when errors occur down there…out of our reach ? The errors may still be in our code but if they are being hit by the DataContractSerializer for instance it’s hard to handle them or even catch them, for that matter. To illustrate the problem and provide a possible solution, here’s an example.
 
First we’ll create a nice simple service library.
 
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization; 

namespace WCFLibrary
{
	[ServiceContract()]
	public interface IMyService
	{
		[OperationContract]
		Thing GetThing(Flavour flavour); 
		
		[OperationContract]
		Thing[] GetOneOfEach();
	} 
	
	public class MyService : IMyService
	{ 
		#region IMyService Members 
		public Thing GetThing(Flavour flavour)
		{
			switch (flavour)
			{
				case Flavour.Orange:
					return new Thing("Ollie", Flavour.Orange);
				case Flavour.Raspberry:
					return new Thing("Roger", Flavour.Raspberry);
				case Flavour.Strawberry:
					return new Thing("Sally", Flavour.Strawberry);
				case Flavour.Banana:
					return new Thing("Bernice", Flavour.Banana);
				default:
					throw new FaultException("No such flavour");
			}
		} 

		public Thing[] GetOneOfEach()
		{
			List<Thing> things = new List<Thing>();
			foreach(Flavour f in Enum.GetValues(typeof(Flavour)))
			{	
				things.Add(this.GetThing(f));
			}
			return things.ToArray();
		} 
		#endregion
	}
	
	public enum Flavour
	{
		Strawberry,
		Raspberry,
		Orange,
		Banana
	} 

	[DataContract]
	public class Thing
	{
		string name;
		Flavour flavour; 
		public Thing(string name, Flavour flavour)
		{
			this.Name = name;
			this.Flavour = flavour;
		}
		
		[DataMember]
		public string Name
		{
			get { return name; }
			set { name = value; }
		} 

		[DataMember]
		public Flavour Flavour
		{
			get { return flavour; }
			set { flavour = value; }
		}
	}
}
Then a host application.
 
using System;
using System.Collections.Generic;
using System.Text; 
using System.ServiceModel;

namespace WCFHost
{
	class Program
	{
		static void Main(string[] args)
		{ 
			using (ServiceHost host = new ServiceHost(
				typeof(WCFLibrary.MyService),	
				new Uri("http://localhost:7001/MyService")))
			{
				host.Closed += new EventHandler(host_Closed);
				host.Open();
				Console.WriteLine(
					"{0} is running at {1}.\nPress any key to close and exit...",
					host.Description.ServiceType.FullName,
					host.BaseAddresses[0].ToString());
				Console.ReadKey(true);
			}	 
		}
		
		static void host_Closed(object sender, EventArgs e)
		{
			Console.WriteLine("{0} closed.",
				((ServiceHost)sender).Description.ServiceType.FullName);
		}
	}
}
With the following config :
 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<system.serviceModel>
		<behaviors>
			<serviceBehaviors>
				<behavior name="MetadataExchange">
					<serviceMetadata httpGetEnabled="true" />
				</behavior>
			</serviceBehaviors>
		</behaviors>
		<services>
			<service behaviorConfiguration="MetadataExchange" 
				name="WCFLibrary.MyService">
			<endpoint address="net.tcp://localhost:7000/MyService" 
				binding="netTcpBinding"
				bindingConfiguration="" name="TCP_7000_MyService"
				contract="WCFLibrary.IMyService" />
			</service>
		</services>
	</system.serviceModel>
</configuration>
And finally having generated a client proxy we can call it like this :
 
using System;
using System.Collections.Generic;
using System.Text; 
using WCFClient.MyService; 
namespace WCFClient
{
	class Program
	{
		static void Main(string[] args)
		{
			using (MyServiceClient proxy = new MyServiceClient())
			{
				foreach (Thing thing in proxy.GetOneOfEach())
				{
					Console.WriteLine("{0} {1}", 
						thing.Name, 
						thing.Flavour.ToString());
				}
			}
			Console.ReadKey(true);
		}
	}
}
Now, we’ll introduce an error in the service that will not be encountered until the Data Contract class is serialized, i.e. it will occur out of scope of the actual service method call. We are going to change this line in our GetThing service method :
 
case Flavour.Banana:
	return new Thing("Bernice", Flavour.Banana);
to this :
 
case Flavour.Banana:
	return new Thing("Bernice", (Flavour)9);
So we are sneakily casting the integer value 9 into the integer-based enum Flavour even though it’s not a valid value of Flavour. I have actually seen a live version of this error where some unexpected integer values got into a database column so the line actually looked more like this :
 
return new Thing("Bernice", (Flavour)reader.GetInt32(3));
When the service is run and called by the client no error is seen in the service even when debugging and the error appears on the client as a timeout. So, imagine this error is intermittent because it only occurs in a very few out of thousands of database records. Some clients are complaining of timeout errors even though the service isn’t busy and there are no errors on the server side. We can’t debug this and there doesn’t seem to be anywhere we can put a try-catch block. What we really need to be able to do is debug into the DataContractSerializer but we can’t. One option is to enable tracing in the service diagnostics. This can be done either using the WCF Configuration Editor :
 
configeditor
 
or by editing the service config :
 
<system.diagnostics>
	<sources>
		<source name="System.ServiceModel" 
			switchValue="Verbose,ActivityTracing"
			propagateActivity="true">
			<listeners>
				<add type="System.Diagnostics.DefaultTraceListener" 
					name="Default">
					<filter type="" />
				</add>
				<add name="ServiceModelTraceListener">
					<filter type="" />
				</add>
			</listeners>
		</source>
	</sources>
	<sharedListeners>
		<add initializeData="c:\temp\app_tracelog.svclog"
			type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
			name="ServiceModelTraceListener" 
			traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, ProcessId, ThreadId, Callstack">
			<filter type="" />
		</add>
	</sharedListeners>
</system.diagnostics>
If we run the service again and make the same call we can view the resulting trace log in the Service Trace Viewer :
 
traceviewer
 
There we can clearly see the exception that occurred. The great thing about the tracing and the Trace Viewer is that if we run tracing on all the consumers and services for a particular activity and enable activity propagation we can get the trace logs from all participants and the Trace Viewer will stitch them together for us so we can see the whole thing end to end and step through.
 
This approach is great in many instances but sometimes when debugging it’s a bit "after the fact" and actually what you’d like is to be be able to set a break point or have the debugger catch it in the act so you can get in and look at the stack.
 
To help with this we really need our own serializer but the serializers (DataContractSerializer and NetDataContractSerializer) are sealed. However, XmlObjectSerializer that they both inherit from is not and it is this type that is used so maybe be can hook in there somehow. I can create a class that inherits from XmlObjectSerializer but I don’t want to write my own serializer…way too much work. However, I can use this class as a wrapper for the actual serializer and delegate to it. This effectively injects my debuggable code into the hard-to-debug WCF plumbing. Here’s my DebuggableDataContractSerializer :
 
public class DebuggableDataContractSerializer : XmlObjectSerializer
{
	private XmlObjectSerializer _Serializer;
	
	public DebuggableDataContractSerializer(XmlObjectSerializer serializerToUse)
	{
		if (serializerToUse == null)
		{
			throw new ArgumentException("Argument cannot be null.", "serializerToUse");
		}
		this._Serializer = serializerToUse;
	}
	
	public override bool Equals(object obj)
	{
		return this._Serializer.Equals(obj);
	}
		
	public override bool IsStartObject(XmlDictionaryReader reader)
	{
		return this._Serializer.IsStartObject(reader);
	}
	
	public override object ReadObject(XmlDictionaryReader reader)
	{
		return this._Serializer.ReadObject(reader);
	}
	
	public override object ReadObject(XmlReader reader)
	{
		return this._Serializer.ReadObject(reader);
	} 
	
	public override object ReadObject(XmlReader reader, bool verifyObjectName)
	{
		return this._Serializer.ReadObject(reader, verifyObjectName);
	}
	
	public override string ToString()
	{
		return this._Serializer.ToString();
	}
	
	public override int GetHashCode()
	{
		return this._Serializer.GetHashCode();
	}
	
	public override bool IsStartObject(XmlReader reader)
	{
		return this._Serializer.IsStartObject(reader);
	}
	
	public override object ReadObject(System.IO.Stream stream)
	{
		return this._Serializer.ReadObject(stream);
	}
	
	public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
	{
		return this._Serializer.ReadObject(reader, verifyObjectName);
	} 

	public override void WriteEndObject(XmlDictionaryWriter writer)
	{
		this._Serializer.WriteEndObject(writer);
	} 
	
	public override void WriteEndObject(XmlWriter writer)
	{
		this._Serializer.WriteEndObject(writer);
	} 
	
	public override void WriteObject(System.IO.Stream stream, object graph)
	{
		this._Serializer.WriteObject(stream, graph);
	} 
	
	public override void WriteObject(XmlDictionaryWriter writer, object graph)
	{
		this._Serializer.WriteObject(writer, graph);
	} 
	
	public override void WriteObject(XmlWriter writer, object graph)
	{
		this._Serializer.WriteObject(writer, graph);
	} 
	
	public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
	{
		this._Serializer.WriteObjectContent(writer, graph);
	} 
	
	public override void WriteObjectContent(XmlWriter writer, object graph)
	{
		this._Serializer.WriteObjectContent(writer, graph);
	} 
	
	public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
	{
		this._Serializer.WriteStartObject(writer, graph);
	} 
	
	public override void WriteStartObject(XmlWriter writer, object graph)
	{
		this._Serializer.WriteStartObject(writer, graph);
	}
}
Now I need to get my serializer swapped out with the real one. I remembered seeing a blog post by Aaron Skonnard way back in April ’06 in which he showed how to substitute the NetDataContractSerializer for the DataContractSerializer so let’s try that here :
 
public class DebuggableSerializer : Attribute, IOperationBehavior
{ 
	public void AddBindingParameters(OperationDescription description,
		BindingParameterCollection parameters)
	{
	}
	
	public void ApplyClientBehavior(OperationDescription description,
		ClientOperation proxy)
	{
		ReplaceDataContractSerializerOperationBehavior(description);
	} 
	
	public void ApplyDispatchBehavior(OperationDescription description,
		DispatchOperation dispatch)
	{
		ReplaceDataContractSerializerOperationBehavior(description);
	} 
	
	public void Validate(OperationDescription description)
	{
	} 
	
	private static void ReplaceDataContractSerializerOperationBehavior(
		OperationDescription description)
	{
		DataContractSerializerOperationBehavior dcsOperationBehavior =
			description.Behaviors.Find<DataContractSerializerOperationBehavior>();
		if (dcsOperationBehavior != null)
		{
			description.Behaviors.Remove(dcsOperationBehavior);
			description.Behaviors.Add(
				new DebuggableDataContractSerializerOperationBehavior(description));
		}
	}
} 
	
public class DebuggableDataContractSerializerOperationBehavior
		: DataContractSerializerOperationBehavior
{
	
	public DebuggableDataContractSerializerOperationBehavior(
		OperationDescription operationDescription) : base(operationDescription)
	{ 
	} 
	
	public override XmlObjectSerializer CreateSerializer(Type type, string name,
	string ns, IList<Type> knownTypes)
	{
		DataContractSerializer dcs = new DataContractSerializer(type, name,
			ns, knownTypes);
		DebuggableDataContractSerializer mdcs = new DebuggableDataContractSerializer(dcs);
		return mdcs;
	} 
	
	public override XmlObjectSerializer CreateSerializer(Type type,
		XmlDictionaryString name,
		XmlDictionaryString ns, IList<Type> knownTypes)
	{
		DataContractSerializer dcs = new DataContractSerializer(type, name, ns, knownTypes);
		DebuggableDataContractSerializer mdcs = new DebuggableDataContractSerializer(dcs);
		return mdcs;
	}
}
Now we add our new custom attribute to our service method and we are good to go.
 
[ServiceContract()]
public interface IMyService
{
	[OperationContract]
	[DebuggableSerializer]
	Thing GetThing(Flavour flavour); 

	[OperationContract]
	[DebuggableSerializer]
	Thing[] GetOneOfEach();
}
 
debugging
 
I have since written an article on how to extend this concept so that it can be used as a ServiceBehavior, ContractBehavior or EndpointBehavior. See Extending the WCF Stack.

You can count on WCF

One of the many great things about WCF which you don’t often hear people talking about is that it comes fully instrumented out of the box. That’s right, the WCF runtime has full instrumentation baked right in, ready for you to use to performance profile and monitor your services and all you have to do to start using it is flick a switch in your service config and run performance monitor (perfmon.exe).
 
If you’ve been a serious windows user for any length of time you will almost certainly have come across perfmon. It is one of the essential tools for quick, easy system and application profiling and many applications (like SQL Server, MSMQ, the CLR, the WCF Runtime, ASP.NET etc) come with their own specific perfmon counters which let you peek under the hood when they are running. Perfmon lets you capture and display data generated by these counters, monitor customized thresholds and take actions when thresholds are breached. It is a massively useful tool.
 
WCF adds four sets of counters to the system. The first three are ServiceModelService, ServiceModelEndpoint and ServiceModelOperation. As you can probably guess from the names, these counters give you different levels of granularity when profiling so you can capture data at the service level, at the endpoint level and at the individual service method level. The final one is SMSvcHost. This is a set of instrumentation counters attached to the .NET TCP Port Sharing Service that lets different services listen on the same port and I will leave this set for another time.
The first three contain the counters listed below (or a subset thereof) :
 

  • Calls
  • Call Duration
  • Calls Failed
  • Calls Failed Per Second
  • Calls Faulted
  • Calls Faulted Per Second
  • Calls Outstanding
  • Instances
  • Instances Created Per Second
  • Queued Messages Dropped
  • Queued Messages Dropped Per Second
  • Queued Messages Rejected
  • Queued Messages Rejected Per Second
  • Queued Poison Messages
  • Queued Poison Messages Per Second
  • Reliable Messaging Messages Dropped
  • Reliable Messaging Messages Dropped Per Second
  • Reliable Messaging Sessions Faulted
  • Reliable Messaging Sessions Faulted Per Second
  • Security Calls Not Authorized
  • Security Calls Not Authorized Per Second
  • Security Validation and Authentication Failures
  • Security Validation and Authentication Failures Per Second
  • Transacted Operatoins Aborted
  • Transacted Operations Aborted Per Second
  • Transacted Operations Committed
  • Transacted Operations Committed Per Second
  • Transacted Operations In Doubt
  • Transacted Operations In Doubt Per Second
  • Transactions Flowed
  • Transactions Flowed Per Second

Now, I mentioned the flick of a switch in the service config to enable this feature which is off by default, so here it is :

 

<system.serviceModel>
    <diagnostics wmiProviderEnabled="false" performanceCounters="All" />
    <!--
      ... your other service config here...
    -->
</system.serviceModel>
 
That’s all that is required. As you can see, it is also possible to provide access to this instrumentation via WMI by switching the value of the wmiProviderEnabled attribute to true so you can write your own monitoring tools and scripts or use other 3rd party tools that use WMI.
 
As an example I have written a very simple service class :
 
using System;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace WCFTestService
{

    [ServiceContract]
    public interface IMyService
    {

        [OperationContract]
        Foo GetFoo();

        [OperationContract]
        void SetFoo(Foo foo);

    }

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class MyService : IMyService
    {

        #region IMyService Members

        public Foo GetFoo()
        {
            System.Threading.Thread.Sleep(200);
            return new Foo("Hello");
        }

        public void SetFoo(Foo foo)
        {
            System.Threading.Thread.Sleep(200);
            Console.WriteLine("Bar = " + foo.Bar);
        }

        #endregion
    }

    [DataContract]
    public class Foo
    {

	public Foo(string bar)
	{
		this.Bar = bar;
	}

	private string _Bar = string.Empty;
	[DataMember]
         public string Bar
         {
            get
            {
                return this._Bar;
            }
            set
            {
                this._Bar = value;
            }
         }
    }
}
 
A simple host executable :
 
using System;
using System.ServiceModel;

namespace WCFTestHost
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(WCFTestService.MyService), new Uri("http://localhost:7001/MyService"));
            host.Open();
            Console.WriteLine("Service Running. Press any key to exit...");
            Console.ReadKey();
        }
    }
}

…and here is the config for the host executable :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<system.serviceModel>
		<diagnostics wmiProviderEnabled="false" performanceCounters="All" />
		<behaviors>	
			<serviceBehaviors>
				<behavior name="MetadataExchange">
					<serviceMetadata httpGetEnabled="true" />
				</behavior>
			</serviceBehaviors>
		</behaviors>
		<services>
			<service behaviorConfiguration="MetadataExchange" 
				name="WCFTestService.MyService">
				<endpoint 
					address="net.tcp://localhost:7000/MyService" 
					binding="netTcpBinding" 
					contract="WCFTestService.IMyService" />
			</service>
		</services>
	</system.serviceModel>
</configuration>
Finally, here is a multi-threaded test consumer for the service:
 
using System;
using System.Collections.Generic;
using System.Threading;
using WCFTestClient.WCFTestService;

namespace WCFTestClient{

	class Program 
	{ 
		static void Main(string[] args) 
		{ 
			try 
			{ 
				WaitCallback fire = delegate(object proxy) { 
					Random r = new Random(System.Environment.TickCount); 
					int i = r.Next(1,3); 
					if (i==1) 
					{ 
						string s = ((MyServiceClient)proxy).GetFoo().Bar; 
						Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + " : " + s); 
					} 
					else 
					{ 
						((MyServiceClient)proxy).SetFoo(new Foo(){Bar = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()}); 
					} 
					
					using (proxy as IDisposable) { } 
				};
					
				Action<MyServiceClient> queueUp = delegate(MyServiceClient proxy) 
				{ 
					ThreadPool.QueueUserWorkItem(fire, proxy); 
				};
				
				List<MyServiceClient> proxies = new List<MyServiceClient>(); 
				
				for (int i = 0; i < 100; i++) { 
					proxies.Add(new MyServiceClient()); 
				} 
				
				proxies.ForEach(queueUp); 
			} 
			catch(Exception ex) 
			{ 
				Console.WriteLine(ex.ToString()); 
			} 
			
			Console.ReadKey(); 
		} 
	}
}
 
So, here are the facts. The service is configured as a singleton. I have inserted a Thread.Sleep(200) into both methods to simulate more complex activity. The client is multi-threaded (via the thread pool) and will create 100 proxies which will randomly call one of the two service methods.
 
What is this likely to mean in performance terms ? Well, firstly services are inherently single threaded meaning that they will only process one call at a time. Therefore, if the call duration on our service is 200 miliseconds (give or take) then the best performance in terms of calls processed per second we can hope for is 5.
 
So let’s profile our service and see if we’re right. Here’s our output :
 
snap01
 
As you can see our 100 proxies made 100 service calls and our singleton made sure that there was only ever one instance processing those calls so instances created per second briefly hit 1.
 
Call duration averaged at .0201 seconds thanks to our Thread.Sleep(200) and as a result calls max per second was 5.009.
 
So what happens if we reduce our call duration to 100 milliseconds (Thread.Sleep(100)) ? What we should see is calls taking half as long and therefore being processed twice as fast.
 
snap02
 
The graph is pretty much identical except that max calls per second is now 10 and the 100 calls have been processed in half the time. As predicted.
 
You can continue this testing increasing the sleep latency up to somewhere between 800 and 900 milliseconds after which the client will throw an exception when it fails to connect with the service with in the default 1 second timeout. After that you either have to increase the timeouts on the consumer, increase the ListenBacklog or MaxConnections (which both default to 10) on the service, reduce the latency of the service, reduce the number of concurrent calls, abandon the singleton in favour of PerCall instance mode or set the ConcurrencyMode of the singleton to Multiple so that it can be accessed by multiple threads.
 
<gotchas>
1) If you want to monitor a specific service instance then the instance must be running when you go to add the counters to the graph.
2) When the service instance stops running the graph will freeze (i.e. stop capturing data) but will restart when you restart the service.
3) Setting up and configuring perfmon graphs can be time consuming so you can save them as counter logs, however you can also save a graph as html which will produce an html page with an embedded perfmon graph complete with all your counters and configurations. You can just open this page in your browser to start monitoring. This also means you can build and keep or publish a library of useful graphs.
</gotchas>
%d bloggers like this: