Note: Using a Console app for hosting should be done for development only !
Now that the disclaimer is out of the way, I currently doing some development work for a customer and I had the need to know every time any of my services receives a call. Since I do my development using a console application for service hosting, it was easy enough to write out to the console from each service operation. Of course, putting code in every method was possible but not only would it be time consuming, to turn it off later would be a pain. The answer needed to be something that gets hit on every operation. Since WCF is essentially one giant inversion of control container, I knew there was a point I could tap into before any operation call.
Enter behaviors !
This turned out to be a multi-step process but once it’s in my library of WCF stuff, I can reuse it and it actually turned out to be quite cool. I’ll explain each step and the reasons for the step.
Step 1 – Write a custom Parameter Inspector
The first class you need to write is one that implements the IParameterInspector which is in the System.ServiceModel.Dispatcher namespace. This class will later be installed in such a way that it gets hit on every operation call. The operation name is one of the arguments you have access to, as well as all the operation arguments which I don’t need in this case.
The methods in this implementation are BeforeCall and AfterCall, and it is the BeforeCall that I’m interested in. The code I’ll place in this method very simply outputs something out to the console using Console.Writeline.
Unfortunately the name of the service is not passed into this method so I need to get that into this class using a custom constructor; you’ll see how I’ll get it to the class later.
Here’s the code for my ConsoleReportInspector class:
public class ConsoleReportInspector : IParameterInspector
{
public ConsoleReportInspector(string serviceName)
{
_ServiceName = serviceName;
}
string _ServiceName = string.Empty;
void IParameterInspector.AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
}
object IParameterInspector.BeforeCall(string operationName, object[] inputs)
{
Console.WriteLine(string.Format("{0} - '{1}.{2}' operation called.", DateTime.Now.ToLongTimeString(), _ServiceName, operationName));
return null;
}
}
Step 2 – Install the class using an Operation Behavior
In order to install the parameter inspector, I’ll need to write a custom operation behavior class. This is a class that implements the IOperationBehavior interface. You need to remove the throw to NotImplementedException from all the methods; and the method of concern here is ApplyDispatchBehavior.
The name of the service being called is accessible through the Parent property of the dispatchOperation argument. I need to instantiate my ConsoleReportInspector class and send the service name into the constructor I added earlier. Afterward, I just need to add my parameter inspector class to the list of parameter inspectors accessible in this behavior.
I also inherited this class from the System.Attribute class so I can use it as an operation behavior attribute on individual operations if I wanted. This is just for flexibility since I plan on hooking this into a service so it affects all operations.
Here’s the code for my ConsoleReportOperationBehaviorAttribute class:
[AttributeUsage(AttributeTargets.Method)]
public class ConsoleReportOperationBehaviorAttribute : Attribute, IOperationBehavior
{
void IOperationBehavior.AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
void IOperationBehavior.ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
void IOperationBehavior.ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
string serviceName = dispatchOperation.Parent.Type.Name;
dispatchOperation.ParameterInspectors.Add(new ConsoleReportInspector(serviceName));
}
void IOperationBehavior.Validate(OperationDescription operationDescription)
{
}
}
As-is, I can simply decorate a service operation with [ConsoleReportOperationBehavior] and if hosted on a console application, you’ll see output to the console when the decorated operation is called.
Step 3 – Install the operation behavior on all operations using a Service Behavior
Next I need to write a service behavior. This is a behavior that gets installed when the service loads (the host is opened). The job of this behavior is to install my operation behavior on all the service operations. A service behavior is a class that implement the IServiceBehavior interface. Like before, I’ll remove the exception calls and once again the method I’m interested in is ApplyDispatchBehavior.
I’m going to iterate through all the endpoints of this service, which are accessible through the serviceDescription argument, and for each endpoint I will iterate through the operations of the contract of each endpoint. To each operation I will add an instance of my ConsoleReportOperationBehaviorAttribute class.
This class too I will inherit from Attribute so I can use it to decorate a service in the case that I wanted to take this approach at some time.
Here’s the code for my ConsoleReportServiceBehaviorAttribute class:
[AttributeUsage(AttributeTargets.Class)]
public class ConsoleReportServiceBehaviorAttribute : Attribute, IServiceBehavior
{
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
foreach (OperationDescription operation in endpoint.Contract.Operations)
operation.Behaviors.Add(new ConsoleReportOperationBehaviorAttribute());
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Step 4 – Apply the service behavior to any service I want programmatically at the host
Whatever approach you take to hosting, all you need to do is access each instance of ServiceHost and add the service behavior to the list of behaviors in the host. I have my own techniques for hosting which include some custom declarative stuff to allow me to turn hosting on and off at will through config. I also like to separate the accumulation of my ServiceHost instances from the actual application that will do the hosting. This way I can move it to a Windows Service when I’m ready to go to production.
Here’s the code that installs the behavior:
ConsoleReportServiceBehaviorAttribute behavior = serviceHost.Host.Description.Behaviors.Find<ConsoleReportServiceBehaviorAttribute>();
if (behavior == null)
host.Description.Behaviors.Add(new ConsoleReportServiceBehaviorAttribute());
The host variable is an instance of ServiceHost. I perform the code above in between a config check so I can toggle it off at will.
The end result is quite cool since when turned on, every call to any of your services will be shown on the console.
Until next time…