Sessions is temporarily moving to YouTube, check out all our new videos here.

Topshelf, the Smart Way to Build Windows Services

Naeem Sarfraz speaking at dotnetsheff in May, 2017
924Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Topshelf is an open source library that allows you to create Micro Services, Windows Services, and the infrastructure around these services. In this talk, we'll look at practical tips that you can use in your day-to-day development.


Transcript


- Thank you guys, I hope that in this short lightning talk that you'll get something really useful and practical that you can use in your day to day development. Who here has heard of Topshelf, can you put your hands up? Hopefully not everybody, that's good, but half of you. Everybody probably recognises this, this is a Windows Services control panel. If you've done any sort of serious .NET development you will have created Windows Services or at least interacted with them. Windows Services, what are they? They are, Windows Services are long-running tasks, usually non-user interactive tasks that are gonna run in the background usually on a server, sometimes on your own local machine. They're kind of jobs that might take a long time. One that you kind of want to just push off to a server to go ahead and do, something like sending a large batch of emails, maybe processing some data import, lots of different types of those type of things. They can, once they set up within Windows, you can trigger them on startup, when a machine starts up, it can start a job. You can run them manually or you can run them based on some sort of event. There's lots of different things you can do. You can do things like, when a user logs in, but that's not really useful and that's not the type of service that we're talking about here, but for example on a machine restart or other events that you can hook into the Windows sort of event system. You can run any of these tasks in the context of a user, it could be running under administrator, or it could run under any user that you actually defined yourself. So they're really useful to kind of offload some of the work that your system, your applications, might need to do. Couple of different ways of managing them. You don't all have to do it through the user interface. There's the sc.exe command line tool, which sometimes a lot of people use NetStart and NetStop, which is a common one, but SC is actually a really useful one because it's designed to manage services and gives you a few more options in terms of what you can and can't do with them. There's also PowerShell, if that's your thing then you can also use PowerShell to manage those services. So going about creating these services in Visual Studio. Now if you've created one of these services, you'll be familiar with this process. There's a template within Visual Studio that you can use to create a service. You go through and select Window Service and it give you something that looks like this. There is a lot of boilerplate code there. First of all there's this Service1.cs, and if you open that file you won't actually get a C# file, what you'll get is a designer surface that you can drag and drop components. I don't know how many people remember that style of development that was kind of like the VB way of doing it where you pull in these components and you drag them onto a design surface and then they get imported there. The C#, the really good stuff, is down in this Designer.cs file and we'll have a look at what that looks like. Sorry, I should say it is essentially a console application, so you see a Program.cs and then the Program.cs has a main function, and so it can kind of kick things off there. So by default you get something like this. Essentially this Service1 class that you get out of the box is the class that's going to do the work that you want doing. Now there is a lot of boilerplate code there. There is a region in some of the other bits of the code where there's a lot of stuff that gets initialised. And one thing that you'll find as soon as you've created this project, hit F5, try and debug the project, and you get this lovely message which essentially says that you can't debug this. You actually have to go and instal this onto your local machine as a service and then go back and attach to the process and then you can begin debugging. It's an absolute nightmare if you have to work with these services. So you come across that, you then discover that you actually have to create an installer. You can do this in a number of different ways. Some people might be familiar with Wix Installers, but the Visual Studio templates do give you one out of the box which uses something called InstallUtil, but basically more boilerplate code that we get which is just not really helping us in terms of actually writing our business function or the task that we actually want to complete. So, enter Topshelf. Topshelf is a library that's been around for a little while. It's an open source library available on GitHub. You can see from the contribution graph right now it kind of started 2012, although I think it might have started actually before 2012 and been hosted somewhere else. But I do wanna show this because it does look like the project has actually kind of fizzled out a little bit, but not quite because Topshelf is actually a pretty good library and anybody who's creating Micro Services, creating Windows Services, is using this in the .NET world to create the infrastructure around creating these services. I noticed on the user group recently on the mailing list that somebody asked is it still actively developed? It was interesting, he said actually it just works, there's not much more to add to the library and you don't often hear that. So don't be fooled by the GitHub graph. So how do we get started with Topshelf? Out of the box, create a standard default console application, import the Topshelf NuGet package. Like all good things, they come in a NuGet package. And then basically this is the absolute minimum amount of code that you need to get started with a service. So you have a host factory, and inside of that host factory we say this class will be the service that we want to run. You can see here straight away you've got a start and a stop method and they correspond in the user interface, the service can start and stop, so when this service begins when this task begins, I want to do X, and then when it stops I can clean up resources, I can handle that situation. There are a lot smaller events that you can tap into. You can pause and continue NET services. But, oh yeah, I was supposed to click back. Yeah, so you've got a host factory, and then this is the bit where you actually write your code to actually do your task. You can see compared to the code that we were looking at before, it is really really terse, it's really really nice, all of the boilerplate code is out of your way and you can focus on the actual code that's actually gonna solve your problem. Once you've compiled that you'll have an EXE like any console application. To instal that as a Windows Service you simply just run on the command line the name of the EXE followed by the word instal or any of the other commands, and it's literally that simple. There's no installer to create, you don't have to mess around with InstallUtil.exe, none of that business. It's just MyService.EXE and then the word instal and that literally will just go ahead and instal the service on your local machine or on whatever you want to run that. If you don't supply any argument and you just run the EXE, it just runs the little bit of code that you've got just like it would do for a normal console application. So that means that you could run your task as a standalone application for a one-off run, or you could run it in the context of a service, so you don't have to instal it as a service and then do the whole dance where you have to attach the process and then debug. You could just run the task by running the EXE, which is one of the really good neat features of Topshelf. Here's an example out of the documentation for a recurring task, so this is like a common pattern that you'll implement where you've got a job that will run, let's say, every minute or every hour or something like that. So here's a little bit more code to set up that now. So we've got, we're telling Topshelf that we're instantiating the TownCrier class, and when it starts call this function and stop with this function, so you can be a bit more explicit about the naming of the functions, or you can just kind of go by convention. And down here you can see you can set things like the name and the description so that when you view the service in the control panel you can see some of the information there. The actual task itself looks something like this. So I've got a, inside of this particular example, we're using a timer object which we start on the start function and stop on the stop function, and each time the timer elapses it just prints out this console. Now obviously nobody will see that if it's running in the context of a service, but it's just to kind of give you an example of a recurring job as it were. And in this example here we've got a timer which is set for one second. This is straight out of the documentation, I wouldn't actually recommend doing a timer service like this. There's a really neat library called Quartz and there's a nice integration with Topshelf for Quartz, and that looks something like this. So I hope everybody can see that, I'll make it a little bit bigger actually. Is that better? Yep. So with this one here, the syntax is slightly, because of the Fluent syntax, it's not the easiest to follow, but essentially what we do is this time we create a job using the Quartz library and then we add a trigger. And the trigger in this scenario is what's called a simple schedule and that simple schedule just says every 60 minutes run this task. You could run it one-off, you've got lots of options here but this one says repeat forever. That's my scheduling done now for that. You can replace that with a Cron, it's got a nice Cron extension as well. I don't know if anybody, if people here are familiar with Cron, but Cron is a Unix-based string which allows you to define a schedule based on some really complex schedules. So you could say, run this job on the first of every year forever, or run this every other second and so on. It gives you so many different options in being able to actually specify a schedule. But that's a much neater way of doing that. And what that then allows you to do is... And down at the bottom, if you can see that, is that you will have a much simpler task class. And you can see there now I've just got a simple execute function and none of that timer business. So that's a neat way of doing that. Within a Topshelf service you might want to host, if you're kind of doing Micro Services, you might want to host Web API within that. Really really easy, that one code snippet there gives you an example of how to do just that. So at the beginning, again I'm setting up my service, my Web API service. And then within my Web API service class, on start I call Webapp.start which is the Katana ON implementation, and that just kicks off a web server. And in this case, I just got the configuration method within here, and you'll recognise this iApp Builder syntax. So now within there, you can then build up your default routes, you can add any pipeline filters, whatever, all that kind of good stuff that you might want for the Web API. That's really easy to set up, a self-hosted Web API. One thing you have to watch out for is when you're self-hosting Web API if you've got some of your references in an external library there's some trickery you have to do there to make sure the library, all the references are correctly imported into this one. Yeah, and that's it, so thank you very much. There's some links there but if you just search for Topshelf C# or something you'll find this project, alright? Thank you.