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
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
- A
utofac.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.
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.
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
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
- Console-App (.NET Core 3.1)
- Is used for the windowsservice
- Open Projectfile - Edit project Sdk to:
<Project Sdk="Microsoft.NET.Sdk.Web">
- 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 !!!
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:
_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
public void ConfigureContainer(ContainerBuilder builder)
{
Bootstrap.RegisterGlobalTypes(builder);
builder.RegisterType<Ip2LocationController>();
builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
builder.RegisterType<ip2locationContext>().InstancePerLifetimeScope();
}
Configure
If HostHttpsRedirect
is set to «1
», the service will redirect to HTTPS automatically.
Settings settings = app.ApplicationServices.GetAutofacRoot().Resolve<Settings>();
if (settings.GetAppSetting("HostHttpsRedirect") == "1")
{
app.UseHttpsRedirection();
}
Start / Stop
_webHost.StartAsync().ConfigureAwait(false).GetAwaiter().GetResult();
_webHost.StopAsync().ConfigureAwait(false).GetAwaiter().GetResult();
ApiController
- Inherits from
ControllerBase
- Set the attributes
Route
& ApiController
- 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.
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.
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