How to Create a Reusable WCF Service Host for Windows Services and Self-Hosting
In this article, I'll show you how to create a reusable service host that can be run from either from the console (self-hosted) or installed as a Windows service. I'll also demonstrate how to use a configuration file instead of code to control which WCF services the host starts. Finally, I'll show how you can make the Windows service installer flexible to allow you to run multiple versions of the service on the same machine. Before getting into implementation details, I'd like to share my motivations behind this solution.
If you have been doing WCF development, you have probably experienced some pain around service hosting. Some of the things I found frustrating on my first WCF project included:
- Making a decision on the type of service host
- Discovering it's more work to host as a Windows service than IIS!
- Installing, starting, stopping and uninstalling a service during development
- Re-writing the same Windows service plumbing for each WCF service
- Having to change code if I wanted to add or remove the WCF services that are hosted
- Trying to host multiple versions of a service on the same machine
Having learned a few things, but by no means an expert, I decided to implement a more flexible and reusable service host to meet these goals:
- Create a single Windows service project that can be shared by all WCF services in the solution
- Keep the service project separate from the service host project
- Ability to run the service host from the console or a Windows service
- Ability to test the service host during development without having to install it
- Ability to host multiple versions of a service on the same machine in production
- Ability to control which WCF services are hosted without having to make code changes
Now that you understand the why, I'll show you how the service host works starting with the entry point: Main(). The output type of a Windows service project is an executable. When the service starts, it calls the executable. Here is the default Main method for a Windows service project:
As you can see this code creates an instance of the service and runs it. To execute this code, you have to install the service and start it. Although it seems like you could run the executable from the command line, you will receive the following error if you try:
So to get around this, we are going to send an argument to the executable that switches it between a Windows service and self-hosting. During development, we can configure Visual Studio to pass the command line argument to test the code. In production, this same mechanism can provide additional diagnostic capabilities. Here is the new implementation of Main():
When run from the command line using the self-hosting argument, the service host starts the WCF services and outputs information to the console:
To run the service host as a Windows service, you first must install it. To do this, copy the output of the service host project to a directory on the server. If there are two versions of the service to run, you'd copy the output to different folders (you also need to configure the services to have different endpoints in the configuration file).
To install the service, run installutil on the service host executable, passing different parameters for the name and displayname, for example:
cd C:\Windows\Microsoft.NET\Framework64\v2.0.50727
installutil /name=CalcV1 /displayname="Calc V1" /description="Sample Calculator Service" "c:\CalcV1\WcfSample.Service.Host.exe"
installutil /name=CalcV2 /displayname="Calc V2" /description="Sample Calculator Service" "c:\CalcV2\WcfSample.Service.Host.exe"
After installing, you will see the services in Windows:
To update the WCF service, stop the Windows service, copy the new WCF service dll and restart the Windows service. There shouldn't be a need to update the service host executable. To uninstall the service, use the /u argument, for example:
installutil /u /name=CalcV1 "c:\CalcV1\WcfSample.Service.Host.exe"
installutil /u /name=CalcV2 "c:\CalcV2\WcfSample.Service.Host.exe"
In the previous section, you saw parameters passed into the service installer (note the forward slash parameter name format such as /name). In the service host project, there is an installer component. In that component, you can access the parameters through the Context object. So instead of hard-coding the service name, we set the service name from the parameters. That's all there is to it.
Now, let's examine how to control which WCF services are hosted using a configuration file. I was initially planning to iterate the service elements in the system.serviceModel section and start all services. You can see an example in this post and this post. I found two problems with this approach. First, you can't control which services to start--like if I wanted to have the configuration for five WCF services, but only start one. Second, the service name element doesn't allow you to specify an assembly name, just the namespace and class. I need the assembly name to load the WCF service type at runtime. To overcome both of these problems, I created a configuration section that allows you to specify which services to start. Here is a sample configuration showing this:
The service container class gets the custom configuration section and iterates through the services list and starts each one:
One of the goals I had for this solution was to separate concerns by keeping the WCF service separate from the service host. In the example below, you can see how the Calculator service is in a separate project than the Host. The only coupling between these two projects is necessary. The Host project has a project reference to the WCF service project so we can load the service assembly at runtime. In this example, there is only one service project, but you can imagine a separate project for each type of service.
This article presented an approach for creating a reusable WCF service host that enables you to specify which WCF services to run using configuration and support hosting multiple versions of a service on the same machine. Hopefully, the techniques presented here improve your WCF service development and deployment experience.
Anonymous
September 20, 2008
PingBack from http://www.easycoded.com/how-to-create-a-reusable-wcf-service-host-for-windows-services-and-self-hosting/Anonymous
September 26, 2008
You've been kicked (a good thing) - Trackback from DotNetKicks.comAnonymous
October 22, 2008
Hi John! I face the same issues your solutions seems great! though, one thing I've noticed - if I want multiple solutions which each one hosts certain services - then I have to copy the Service.Host files (or am I wrong?) - any way to put these files in one library and re-use it? Thanks, ShlomiAnonymous
January 16, 2009
Simple, but great :) I added an IsEnabled property to the service element. It would be nice if this was the template for a Windows Service as it greatly simplifies the development process with an F5 deployment.Anonymous
February 22, 2009
your solutions seems great ! I was really waiting for this solution for the problem I was facing. I hope this will work in re-useing procees too.Anonymous
June 01, 2010
Great article, but a shame you don't talk about setspn and some of the problems which may be encountered. Since self-hosting will more than likely lead to using the setspn executableAnonymous
April 10, 2014
Interesting article. After hosting the WCF Services on Windows Service how would i be able to access it ?