- Install
TopShelf
from nuget. - Create a class which has the methods to be executed when the windows service starts and stops (There can be other situations too, such as pause, continuing and, all)
- In the main method use the
TopShelf
API to configure the service behaviour.
If you are using the .net core you'll find that the console applications are getting built as a dll
not as an exe
as before. So to install the service we cannot use the same command as we did when TopShelf
was used with .net framework.
([exe name] install
)
Follow the below mentioned steps to install the windows service,
- Browse to the location where the
dll
is built. - Use the below command to install the service.
dotnet [SERVICE NAME WITH THE EXTENSION DLL] install --localsystem
But just by executing the command above the windows service will not work. If you start to start
or stop
the service from SCM, you'll get an error.
- Open registry editor.
- Browse to
HKEY_LOCAL_MACHINE\SYSTEM\CONTROLSET001\SERVICES\[YOUR SERVICE NAME]
and open theImagePath
variable and modify the variable value to,
"c:\program files\dotnet\dotnet.exe" "[FULL PATH TO THE DLL]" -displayname "[LEAVE THE EXISTING VALUES]" -servicename "[LEAVE THE EXISTING VALUES]"
Reference - Read the
boekabart
comment on this
When implementing DI in a console (or a non web) application you'll need at least the below nuget libraries.
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Options.ConfigurationExtensions
Always it's better to move the dependency registrations to a different class and, also to have separate your different configurations into their
own config files / sections. In here I have created a class called Bootstrapper
and I have created a separate JSON
configuration file called appsettings.json
.
{
"ToDoApi": {
"Url": "some url"
}
}
- Make sure you set this file's properties as to
Copy Always
(or to a setting which it will be available in the same directory as the executable)
The above are the pre-requisites which you need to do have dependency injection in the application. The next part is the code part where you register the dependencies.
- First you need the actual configuration object where your custom configuration data is present. That object is represented by the
IConfigurationRoot
.
private static IConfigurationRoot GetConfiguration()
{
var configuration = new ConfigurationBuilder()
//.SetBasePath(Directory.GetCurrentDirectory())
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
return configuration;
}
IMPORTANT
-
There is a contextual difference between using
Directory.GetCurrentDirectory
and usingSystem.AppDomain.CurrentDomain.BaseDirectory
.System.AppDomain.CurrentDirectory
- Always represents an app domain. Hence it will always return the path where the exe/dll is located.Directory.GetCurrentDirectory
- By looking under the hood of this method it's clear that it returns theEnvironment.CurrentDirectory
. Hence it will return a value which is "specific" to the platform or the environment and, this value can change overtime as well.- References
-
Then load the configuration data and register them as a dependency.
var configuration = GetConfiguration();
services.Configure<ToDoApiConfig>(configuration.GetSection("ToDoApi"));
services.AddSingleton(provider =>
{
var apiConfig = provider.GetRequiredService<IOptions<ToDoApiConfig>>().Value;
return apiConfig;
});
- Register the other dependencies as you would usually.
services.AddHttpClient<ITodoApiClient, TodoApiClient>();
services.AddTransient<IInvoiceProcessor, InMemoryInvoiceProcessor>();
IServiceCollection.AddHttpClient
extension method is coming from theMicrosoft.Extensions.Http
nuget library. In this application it's required because it has a typed HTTP client to communicate with the external web API.
For this we are going to use Quartz
.
In Quartz
terms, what we implement are jobs
. These jobs are the little minions which actually gets executed in a scheduled fashion through Quartz
.
But remember we use DI here and, we want the Quartz related code to follow the same practices.
These are the main points which we need to consider when using Quartz
to be crafted to our solution.
- Any job which you want to schedule through
Quartz
must implement theirIJob
interface. - In
Quartz
the jobs are created through a factory class which implementsIJobFactory
. So in here we'll implement a custom job factory, which will use theIServiceProvider
instance to create the job as requested. - The jobs are scheduled through a job scheduler factory which implements
ISchedulerFactory
.Quartz
already have a default implementation of it which is calledStdSchedulerFactory
and, that's the one which we'll be using as our scheduler. - Using
Quartz
you can create different types of triggers. Simple triggers such as executing in a specific interval to something a bit more complex which could be represented by aCRON
expression. In here we are using a simple trigger. - Refer the
Bootstrapper
class to refer how the dependencies are registered.