Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Kestrel

Easily Creating Windows Services with Topshelf

4.24/5 (13 votes)
3 Mar 2020CPOL5 min read 16.1K  
A small compact tutorial for creating Windows services in .NET Core - Examples containing scheduled jobs / Entitiy Framework Core / Hosting a WebApi
This article shows you how to run a simple Windows service using a Scheduler, Entity Framework Core or hosting a WebApi with kestrel server.

Introduction

The core library for these examples is Topshelf. It provides a very simple way to get started with winservices. This article contains examples for winservices which should execute scheduled jobs or run a RestApi.

All examples are using Dependency Injection, if you are not familiar with it, here are some further readings for Dependency injection and the Autofac framework.

Getting Started

  • All examples are based on .NET Core 3.1

Example 1 - Basic winservice

Required NuGet-Packages

Topshelf

  • Executing the EXE will run the service in a console
  • Windowsservice can be installed via cmd.exe (Run as Administrator) - Service.exe install
  • For uninstallation, use Service.exe uninstall

Bootstrap.cs

This class is for registering the types for the dependency Injection. In this example, the BasicService, which contains the logic for the service, must be registered.

Example 2 - Scheduledservice

Required NuGet-Packages

  • Topshelf
  • Topshelf.Autofac
  • Quartz
  • Autofac.Extras.Quartz
  • Microsoft.Extensions.Configuration.Json

Topshelf

  • Executing the EXE will run the service in a console
  • Windowsservice can be installed via cmd.exe (Run as Administrator) - Service.exe install
  • For uninstallation, use Service.exe uninstall

Bootstrap.cs

This class is for registrering the types for the dependency Injection.

  • Registration of Servicecontrollers
  • Registration of IConfiguration - Provides reading the appsettings.json
  • Registration of Settings - Helperclass for easier access of the configuration
  • Registration of the QuartzModule
  • Registration of the Controller

Quartz

Logging

If jobs are not executed, turn on the logging of Quartz. The QuartzConsoleLogProvider.cs implements a logger which is logging to the console.

Registration of the logprovider

C#
LogProvider.SetCurrentLogProvider(new QuartzConsoleLogProvider());
Jobs

Job are the task which your service should run. These must implement the IJob interface. If this job needs some additional data, you can use the JobDataMap. (See «QuartzController.cs» and MyJob.cs)

Triggers

Quarzt provides several triggers for executing jobs at a certain time. Examples are provided in QuartzController.cs:

  • Execute job once.
  • Executing job with interval.
  • Delete a job from queue.

Documentation of triggers

Example 3 - ScheduledEFCoreWinservice

Required NuGet-Packages

  • Topshelf
  • Topshelf.Autofac
  • Quartz
  • Autofac.Extras.Quartz
  • Microsoft.Extensions.Configuration.Json
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

Topshelf

  • Executing the EXE will run the service in a console
  • Windowsservice can be installed via cmd.exe (Run as Administrator) - Service.exe install
  • For uninstallation, use Service.exe uninstall

Bootstrap.cs

This class is for registering the types for the dependency Injection.

  • Registration of Servicecontrollers
  • Registration of IConfiguration - Provides reading the appsettings.json
  • Registration of Settings - Helperclass for easier access of the configuration
  • Registration of the QuartzModule
  • Registration of the Controller
  • Registration of the database context

EF Core with DependencyInjection

Database

The example database is an import of the free Ip2Location database. For instructions on downloading and importing to MS-SQL, check the following link: IP2Location™ LITE IP-COUNTRY Database.

Bootstrap

InstancePerLifetimeScope - the context will be created for each job and after the job is done, it will be disposed.

C#
builder.RegisterType<ip2locationContext>().InstancePerLifetimeScope();
builder.RegisterType<ip2locationWriteContext>().InstancePerLifetimeScope();
Context

The configuration will be automatically injected because of the constructor ip2locationContext(IConfiguration configuration). The configuration is used for reading the connection string.

C#
public partial class ip2locationContext : DbContext
{
    protected readonly IConfiguration _configuration;

    public ip2locationContext(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(_configuration.GetConnectionString("CTX"));
        }
    }
}
ip2locationContextFactory

If you want EF-Core migrations, you should implement this class. Otherwise, the EF-Core tools won´t find your connection string.

Quartz

Logging

If jobs are not executed, turn on the logging of Quartz. The QuartzConsoleLogProvider.cs implements a logger which is logging to the console.

Registration of the logprovider

C#
LogProvider.SetCurrentLogProvider(new QuartzConsoleLogProvider());
Jobs

Job are the task which your service should run. These must implement the IJob interface. If this job needs some additional data, you can use the JobDataMap. (see «QuartzController.cs» and MyJob.cs)

Triggers

Quarzt provides several triggers for executing jobs at a certain time. Examples are provided in QuartzController.cs:

  • Execute job once.
  • Executing job with interval.
  • Delete a job from queue.

Documentation of triggers

Example 4 - WebapiWinservice

Required NuGet-Packages

  • Topshelf
  • Topshelf.Autofac
  • Microsoft.Extensions.Configuration.Json
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.AspNetCore
  • Microsoft.AspNetCore.Core
  • Microsoft.AspNetCore.Server.Kestrel.Core
  • Microsoft.Extensions.Hosting
  • Microsoft.Extensions.Logging.Configuration

Topshelf

  • Executing the EXE will run the service in a console
  • Windowsservice can be installed via cmd.exe (Run as Administrator) - Service.exe install
  • For uninstallation, use Service.exe uninstall

Project-setup

  1. Console-App (.NET Core 3.1)
    • Is used for the windowsservice
    • Open Projectfile - Edit project Sdk to: <Project Sdk="Microsoft.NET.Sdk.Web">
  2. Classlibrary (.NET STandard 2.0)
    • If responseobjects are desired for a C# application, create a class library. Alternatively, you can just use dynamic objects parsed from JSON.

appsettings.json

  • The section Kestrel and Logging must be configured.
  • "AllowedHosts": "192.168.130.16;test.example.com" - with that configuration, you can define from which hosts the WebApi can be accessed.

Publish Hints

  • AllowedHosts should be configured
  • If API should only be available internally, configure Windows Firewall

Debugging

!!! Do not execute with IIS-Express !!!

debugging

Bootstrap.cs

This class is for registering the types for the dependency Injection.

BuildContainer
  • Registration of the servicecontroller
RegisterGlobalTypes

These types must be registered two times. Once for the ASP.NET webhost and once for your the service controller.

  • Registration of IConfiguration - Provides reading the appsettings.json
  • Registration of Settings - Helperclass for easier access of the configuration

WebApi

Initialization

Building the webhost

Define the settings for the kestrel server:

C#
_webHost = Host.CreateDefaultBuilder()
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
        webBuilder.ConfigureKestrel(serverOptions =>
        {
            serverOptions.Limits.MaxConcurrentConnections = 100;
            serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
            serverOptions.Limits.MaxRequestBodySize = 30 * 1024 * 1024;
            serverOptions.Limits.MinRequestBodyDataRate =
                new MinDataRate(bytesPerSecond: 100,
                    gracePeriod: TimeSpan.FromSeconds(10));
            serverOptions.Limits.MinResponseDataRate =
                new MinDataRate(bytesPerSecond: 100,
                    gracePeriod: TimeSpan.FromSeconds(10));
            serverOptions.Limits.KeepAliveTimeout =
                TimeSpan.FromMinutes(2);
            serverOptions.Limits.RequestHeadersTimeout =
                TimeSpan.FromMinutes(1);
        });
    })
    .UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .Build();
Startup.cs

ConfigureContainer

Registration of the types for Autofac

C#
public void ConfigureContainer(ContainerBuilder builder)
{
    Bootstrap.RegisterGlobalTypes(builder);
    builder.RegisterType<Ip2LocationController>();
    // is used for determining the client ip
    builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
    // context per request
    builder.RegisterType<ip2locationContext>().InstancePerLifetimeScope();
}

Configure

If HostHttpsRedirect is set to «1», the service will redirect to HTTPS automatically.

C#
Settings settings = app.ApplicationServices.GetAutofacRoot().Resolve<Settings>();
if (settings.GetAppSetting("HostHttpsRedirect") == "1")
{
    app.UseHttpsRedirection();
}
Start / Stop
C#
// Start
_webHost.StartAsync().ConfigureAwait(false).GetAwaiter().GetResult();
// Stop 
_webHost.StopAsync().ConfigureAwait(false).GetAwaiter().GetResult();
ApiController
  1. Inherits from ControllerBase
  2. Set the attributes Route & ApiController
  3. Implement HttpGet / HttpPost methods
Example C# Client

Project «WebapiWinserviceClientExample».

EF Core with DependencyInjection

Database

The example database is an import of the free Ip2Location database. For instructions on downloading and importing to MS-SQL, check the following link: IP2Location™ LITE IP-COUNTRY Database.

Bootstrap

InstancePerLifetimeScope - the context will be created for each job and after the job is done, it will be disposed.

C#
builder.RegisterType<ip2locationContext>().InstancePerLifetimeScope();
builder.RegisterType<ip2locationWriteContext>().InstancePerLifetimeScope();
Context

The configuration will be automatically injected because of the constructor ip2locationContext(IConfiguration configuration). The configuration is used for reading the connection string.

C#
public partial class ip2locationContext : DbContext
{
    protected readonly IConfiguration _configuration;

    public ip2locationContext(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(_configuration.GetConnectionString("CTX"));
        }
    }
}
ip2locationContextFactory

If you want EF-Core migrations, you should implement this class. Otherwise, the EF-Core tools won't find your connection string.

History

  • 2nd March, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)