Friday, 21 August 2009

Hosting multiple WCF Services under a single Windows Service

I am often asked how to host multiple services under a single windows service and through trial and error have been streamlining this process for clients.

I thought I'd blog an example here for reference:

class Program {
static void Main() {
if (Environment.UserInteractive) {
ServiceManager serviceManager = new ServiceManager();
serviceManager.OpenAll();
Console.ReadKey();
serviceManager.CloseAll();
}
else
ServiceBase.Run(new WindowsService());
}
}

public class WindowsService : ServiceBase
{
public static string WindowsServiceName = "Windows Service Name";
public static string WindowsServiceDescription = "Windows Service Description";
public static string WindowsServiceUsername = @".\username";
public static string WindowsServicePassword = "password";

private readonly ServiceManager serviceManager = new ServiceManager();

private readonly IContainer components = new Container();

protected override void Dispose(bool disposing) {
if (serviceManager != null) serviceManager.CloseAll();
if (disposing && (components != null)) components.Dispose();
base.Dispose(disposing);
}

public WindowsService() {
ServiceName = WindowsServiceName;
CanStop = true;
}

protected override void OnStart(string[] args) {
base.OnStart(args);
serviceManager.OpenAll();
}

protected override void OnStop() {
serviceManager.CloseAll();
base.OnStop();
}
}

public class ServiceManager {
readonly List serviceHosts = new List();

public void OpenAll() {
OpenHost();
OpenHost();
...
}

public void CloseAll() {
foreach (ServiceHost serviceHost in serviceHosts)
serviceHost.Close();
}

private void OpenHost() {
Type type = typeof(T);
ServiceHost serviceHost = new ServiceHost(type);
serviceHost.Open();
serviceHosts.Add(serviceHost);
}
}

///
/// Enables application to be installed as a Windows Service by running InstallUtil
///

[RunInstaller(true)]
public class WcfServiceHostInstaller : Installer {
public WcfServiceHostInstaller() {
Installers.Add(new ServiceInstaller
{
StartType = ServiceStartMode.Automatic,
ServiceName = WindowsService.WindowsServiceName,
Description = WindowsService.WindowsServiceDescription
});
Installers.Add(new ServiceProcessInstaller { Account = ServiceAccount.User, Username = WindowsService.WindowsServiceUsername, Password = WindowsService.WindowsServicePassword });
}
}


And some configuration:

- Here, the binding & behaviour configuration is shared across services but you may need different configurations for different types of services.
- I use different ports for different services, but you don't have to.

<system.serviceModel>
<services>
<service behaviorConfiguration="DefaultBehavior" name="Namespace.Service1">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="TCPBindingConfig" name="TCPEndpoint" contract="Namespace.IService1" />
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="TcpMetaData" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8001/Namespace/Service1" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="DefaultBehavior" name="Namespace.Service2">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="TCPBindingConfig" name="TCPEndpoint" contract="Namespace.IService2" />
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="TcpMetaData" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8002/Namespace/Service2" />
</baseAddresses>
</host>
</service>
...
</services>
<bindings>
<netTcpBinding>
<binding name="TCPBindingConfig" maxBufferSize="5242880" maxReceivedMessageSize="5242880">
<readerQuotas maxStringContentLength="5242880" />
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="DefaultBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="21" maxConcurrentSessions="50" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

4 comments:

jtango said...

Fantastic post. Helped me out a lot. Thanks

Anonymous said...

This is great. I searched for a long time to find something like this. There don't seem to be any decent WCF examples available.

Snakefoot said...

Is it possible to start a WCF service for each service defined in the app.config ?

Dave K said...

Your blog software has eaten the less-than and greater-than characters on your OpenHost definition and calls.

Thanks for the template, I've been trying to get something similar working.

When you say "I use different ports for different services, but you don't have to." do you mean that this will work as-is if the base address ports for each service are the same? I've had difficulty getting that to work (serviceHost.Open() fails because the port is in use), though I haven't tested this with the code you provide here.