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:
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:
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:
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.
Get the Code