Configuring Topshelf Using A StructureMap Container

At Dovetail we use StructureMap a lot. Lately we’ve also been using Topshelf a lot too. This post takes a look at how you can marry these two technologies together. We will be using the StrucutreMap container to configure which services get started by Topshelf at runtime.

Simple Services


Let’s start simple. We need a way to identify which configured types want to get configured to be spun up by Topshelf. To accomplish this I created an interface called ISimpleService which includes only the Start and Stop methods that Topshelf uses to manage the services lifetime.


public interface ISimpleService
  void Start();
  void Stop();


Next up we’ll update and expand the example from my previous post Controlling Application Lifetime In Topshelf to have it implement the ISimpleService. We’ll also add another service in charge of spamming the log file.


public class LogSpamService : ISimpleService
  private readonly ILogger _logger;
  private Timer _timer;
  public LogSpamService(ILogger logger)
    _logger = logger;
  public void Start()
    _timer = new Timer(SpamTheLog, this, TimeSpan.Zero, TimeSpan.FromSeconds(1));
  private void SpamTheLog(object state)
    _logger.LogInfo("spam spam spam");
  public void Stop()

Configuring Simple Services


Here comes the magic. I created a handy Topshelf configuration extension method for registering all the types in the container registered implementing the ISimpleService interface.


public static class TopShelfRunnerConfiguratorExtensions
  public static void ConfigureSimpleServices(this IRunnerConfigurator configurator, 
IContainer container, ILogger logger)   {     var simpleServiceInstances = container.Model.InstancesOf();     foreach (var simpleServiceInstance in simpleServiceInstances)     {       var requiredServiceType = simpleServiceInstance.ConcreteType;         logger.LogDebug("Configuring runtime to manage the
lifecycle of the service {0}.", requiredServiceType);         configurator.ConfigureService(service =>       {         service.HowToBuildService(name =>
container.GetInstance(requiredServiceType));         service.WhenStarted(s => s.Start());         service.WhenStopped(s => s.Stop());       });     }   } }


This extension method will be used to configure Topshelf to start up all of the ISimpleService types present. Next all that is left to do is update the configuration code to use our new extension method.

Starting Up The Application

internal class ProgramExample
  private static void Main(string[] args)
    Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
    var container = new Container(cfg =>
    var logger = container.GetInstance
();       var runConfiguration = RunnerConfigurator.New(config =>     {       config.ConfigureSimpleServices(container, logger);     });       container.Inject(runConfiguration.Coordinator);       Runner.Host(runConfiguration, args);   } }


First I tell StructureMap to scan the assembly and register all the ISimpleServices it finds. Then we use the Topshelf extension method to configure all the simple services. In this case we have two services present LogSpamService and the HealthMonitoringService.

Let It Rip



The application output shows the two services being started. A lot of spam being logged and eventually the health monitor shuts the application down.



The real power behind using an IoC container to drive the configuration of the Topshelf application host is that we can now add a service to be started at runtime by simply implementing a single interface.


A more advanced scenario in use at Dovetail is to only start up the services which are necessary. We have an application with a plug-in architecture where there may be services present in the application that are not necessary because the plug-ins that consume the output of the service are not in use.