To top off my previous post with an example on how almost any kind of application can be hosted as a Windows Service, I’ll show an example on how Topshelf can be used to host an IoC container.
As most developers probably know, an IoC container functions as a logical application container of sorts, helping with creating stuff when stuff needs to be created, and disposing stuff when stuff needs to be disposed, keeping whatever needs to be kept alive alive for the entire duration of the application lifetime.
This way, when we have this Topshelf-IoC-container framework in place, we can use it to activate all kinds of applications.
I’ll show how it’s done with Castle Windsor, but I think it should be fairly easy to port this solution to any other IoC container.
Create a service class that holds the container
First, let’s create a class that implements the Start/ Stop methods of ServiceControl – this class will initialize its Windsor container when it starts, and dispose it when it stops.
I’ve added some logging for good measure – when you’re a backend kind of guy, you come to appreciate a systematic and consistent approach towards logging in your Windows services, and I’m no exception.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
class RobotService : ServiceControl { static readonly Logger Log = LogManager.GetCurrentClassLogger(); readonly WindsorContainer _container = new WindsorContainer(); public bool Start(HostControl hostControl) { try { Log.Info("Initializing container"); _container.Install(FromAssembly.This()); return true; } catch (Exception exception) { Log.Error("An error occurred during intialization: {0}", exception); return false; } } public bool Stop(HostControl hostControl) { try { Log.Info("Disposing container"); _container.Dispose(); return true; } catch (Exception exception) { Log.Error("An error occurred during container disposal: {0}", exception); return false; } } } |
Use Topshelf to activate the service class
Now we can have our RobotService activated as a Windows Service by adding the following C# spells:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Program { static readonly Logger Log = LogManager.GetCurrentClassLogger(); static void Main() { AppDomain.CurrentDomain.UnhandledException += (o, ea) => Log.Fatal("Unhandled exception: {0}", ea.ExceptionObject); HostFactory.Run(f => { f.UseNLog(); f.Service<RobotService>(); }); } } |
Please note that Topshelf needs a little extra help with logging unhandled app domain exceptions, so I’ve added a little bit of Log.Fatal at the top.
Add components to the container to actually do something
Now, just to show some real action, I’ve added a single Windsor installer that looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class TestInstaller : IWindsorInstaller { static readonly Logger Log = LogManager.GetCurrentClassLogger(); public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For<IDisposable>() .Instance(CreateAndStartTestTimer(interval: TimeSpan.FromSeconds(1))) .Named("TestTimer") ); } Timer CreateAndStartTestTimer(TimeSpan interval) { var timer = new Timer(interval.TotalMilliseconds); timer.Elapsed += (o, ea) => Log.Info("woooH0000! It's going great!!1"); timer.Start(); return timer; } } |
As you can see, it will start a System.Timers.Timer, configured to periodically log a cheerful statement to the log and shove it in the container to be disposed when the application shuts down. It shouldn’t take too much imagination to come up with more useful and realistic tasks for the timer to perform.
This is actually all there is to it! Of course it can be customized if you want, but what I’ve shown is the only stuff that is necessary to create a full-blown Windows Service that manages the lifetime of application components and logs its work in a way that makes the service monitorable, e.g. by Microsoft SCOM and/or Splunk or whichever way you like to throw your logs.
As a public service, I’ve put this small example project in a GitHub repository for your cloning pleasure. Also, if you have any comments, please don’t hesitate to send them my way. Pull requests are also accepted 🙂