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.
Why I Developed 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
How it Works
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():
Running the Service Host from the Command Line
When run from the command line using the self-hosting argument, the service host starts the WCF services and outputs information to the console:
Running the Service Host as a Windows Service
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"
Using the Same Service Host for Multiple WCF Services
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.
Controlling Which WCF Services are Hosting Using a Configuration File
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:
Separating the WCF Service from the Service Host
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.
Conclusion and Link to Download the Code
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.