How TopShelf solves a specific problem very elegantly.

A lot of our architecture is based on windows services. One of the issues with this setup is that we would have to install those windows services on our developer machines. Something we are not really fond of, especially since they won't be used for the bigger part of our work. We just want to run the services related to the specific problem we are trying to solve.

To work around this inconvenience, we would create a project containing some general code and next to that project there is a console application project and a windows service project. You can imagine that this causes a lot of overhead in the solution. For each service we would have three projects instead of only the one we actually need. I have seen this approach before and one more than one occasion, and was never really a big fan of it.

But now we have a more elegant solution. It's name is TopShelf.

How to use TopShelf

I won't explain all  the details. It's definitely worth reading the documentation at http://topshelf-project.com/.
What you will read here is a brief description of how we use it.

Creating a new project

The project we create is a 'console application' where Program is our startup object. My apologies for the nasty black marking (using Snipping Tool and I rather don't mention too much internal details).

Where to get TopShelf?

We get TopShelf from Nuget. Simply use the 'Package Manager UI' or run the following command in the 'Package Manager Console':

Install-Package Topshelf -Version 3.1.1

Creating the service

Before telling you how host your services using the Topshelf Hosting Framework we have to create the service.
The service looks like this.

namespace <YOURNAMESPACE>
{
    public class Service
    {
        public Service()            
        { 
        }

        public void Start()
        {
             /* DO WORK HERE */
        }

        public void Stop()
        {
            /* DO CLEANUP HERE */
        }
    }
}

It's important to properly dispose of objects you created or threads you have started when calling Stop(). On more than one occasion we write multithreaded services and if we don't cleanup threads nicely in this phase, we might get sharing violations when we try to copy over a new version of the service.

Hosting the service

The final thing we need to do is the actual hosting of this service using TopShelf. This happens in the Program.cs file.

using Topshelf;

namespace <YOURNAMESPACE>
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
            HostFactory.Run(x =>
            {
                x.Service<Service>(s =>
                {
                    s.ConstructUsing(() =>
                    {
                        Bootstrapper.Run();
                        return new Service();
                    });
                    s.WhenStarted(tc => tc.Start());
                    s.WhenStopped(tc => tc.Stop());
                });

                x.SetDisplayName("<YOUR SERVICE DISPLAY NAME>");
                x.SetServiceName("<YOUR SERVICE NAME>");
                x.SetDescription("<YOUR SERVICE DESCRIPTION>");

                // Service Identity

                x.RunAsLocalSystem();

                // Advanced Settings

                //// Specifies that the service supports the shutdown service command, allowing the services control manager to quickly shutdown the service.
                x.EnableShutdown();

                // Service recovery plan
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(1); // restart the service after 1 minute
                });
            });
        }
    }
}

It's very important that you run all initialization logic for your service in the ConstructUsing block. Running it outside of the Topshelf initialization might lead to unexpected behavior.
Our initialization happens in a Bootstrapper static class. So this is called first thing when entering the ConstructUsing method.

All other options are described either in code, and a lot more can be found on the TopShelf documentation site.

And the result is what exactly?

As a developer we can simply debug the console application. No windows service is installed. When working on a specific issue we can run the services by running their console variant. On our servers we can copy the bin output and by running a simple command the service is installed and configured. No more need to create a service installation file.

"Path to your.exe" install

Other calls we often make are 'stop' and 'start'. This is very handy stuff when you integrate this in your continuous delivery story.