Skip to content
This repository was archived by the owner on Jun 30, 2021. It is now read-only.

Simplify.WindowsServices

Alexanderius edited this page Jun 23, 2019 · 33 revisions

Simplify.WindowsServices Documentation

Provides BasicServiceHandler, SingleTaskServiceHandler, MultitaskServiceHandler windows-services base classes, ServiceInstallerBase installation base class.

Allows you to simply create and use windows services. Every user class instance will be instantiated using Simplify.DI IOC container which provides DI by default for your windows services.

Available at NuGet as binary package

Principle of work

To use Simplify.WindowsServices, you should create a class with Run method in it and pass this class to handler as a type parameter. Run method will be invoked by the handler class (the types of handles are specified below), in this method you should start your windows service processing logic. Your class instance will be created using IOC container Simplify.DI, so you can registed any types from which your class depends via DIContainer.Current and they will be automatically resolved.

Run method can be Run() or Run(string ServiceName) if you want to get service name in it.

Quick start

There a templates package available at nuget.org for Simplify.WindowsServices.

Install the Simplify.WindowsServices templates packages:

dotnet new -i Simplify.WindowsServices.Templates

Create an initial Simplify.WindowsServices based project:

dotnet new simplify.windowsservice -n HelloWorld

BasicServiceHandler

BasicServiceHandler class is best suitable for services which is working without timers, for example, TCP server or client.

Example

MyClass.cs

public class MyClass
{
    public void Run()
    {
        // Some task
    }
}

Program.cs

static void Main(string[] args)
{
    new BasicServiceHandler<MyClass>(true).Start(args);
}

The true parameter indicates what MyClass class will be automatically registered in DIContainer.Current like DIContainer.Current.Register<MyClass>(); otherwise you should do it manually:

static void Main(string[] args)
{
    DIContainer.Current.Register<MyClass>();
    new BasicServiceHandler<MyClass>().Start(args);
}

SingleTaskServiceHandler

SingleTaskServiceHandler class is best suitable for single task operation which should periodically run by a timer.

You can specify timer intervals in seconds or just set the Ncrontab expression for timer. This options should be set via configuration. If no settings specifed in config, then the timer will be executed once every minute. If both timer interval and crontab expression specified in the config, then the crontab expression will be used instead.

Example

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="ServiceSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>

    <ServiceSettings>
        <!-- Specifying crontab expression -->
        <add key="CrontabExpression" value="* * * * *" />

        <!-- Or specifying timer interval in seconds -->
        <add key="ProcessingInterval" value="30" />
    </ServiceSettings>
</configuration>

For crontab expression examples see

Multiple crontab expressions can be specified, for example: 30 14 * * *|45 1 * * *|0 12 * * Mon

SingleTaskServiceHandler working example

MyClass.cs

public class MyClass
{
    public void Run()
    {
        // Some task
    }
}

Program.cs

static void Main(string[] args)
{
    new SingleTaskServiceHandler<MyClass>(true).Start(args);
}

MultitaskServiceHandler

MultitaskServiceHandler class is best suitable for multiple task services.

It is the same as SingleTaskServiceHandler but allows you to specify multiple working classes/methods. You can add multiple jobs with different timer intervals/crontab expressions.

You can use single class with multiple methods, each specified method will be lauched by respective task (new class instance will be created), or just use multiple classes.

Example

MultitaskServiceHandler working example

Program.cs

static void Main(string[] args)
{
    // Handler creation
    var handler = new MultitaskServiceHandler();

    // Jobs addition

    // If configuration section is not specified then 'YourClassName + Settings' section name will be used
    handler.AddJob<TaskProcessor1>(true);

    // Manually specified section name and invoke method name
    handler.AddJob<TaskProcessor1>("TaskProcessor1SecondTaskSettings", "RunTask2");

    // Registering class manually

    DIContainer.Current.Register<TaskProcessor2>();
    handler.AddJob<TaskProcessor2>();

    handler.Start(args);
}
TaskProcessor1.cs
public class TaskProcessor1
{
    public void Run()
    {
        Debug.WriteLine("TaskProcessor1 Run executed");
    }

    public void RunTask2()
    {
        Debug.WriteLine("TaskProcessor1 RunTask2 executed");
    }
}
TaskProcessor2.cs
public class TaskProcessor2
{
    public void Run()
    {
        Debug.WriteLine("TaskProcessor2 Run executed");
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="TaskProcessor1Settings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <section name="TaskProcessor1SecondTaskSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <section name="TaskProcessor2Settings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>

    <TaskProcessor1Settings>
        <add key="CrontabExpression" value="*/2 * * * *" />
    </TaskProcessor1Settings>

    <TaskProcessor1SecondTaskSettings>
        <add key="ProcessingInterval" value="30" />
    </TaskProcessor1SecondTaskSettings>

    <TaskProcessor2Settings>
        <add key="ProcessingInterval" value="45" />
    </TaskProcessor2Settings>
</configuration>

Services installation/uninstallation

If you passes args parameter from your Main method to hander Start method, then you can install/uninstall service by the service command line parameters.

Installation example

D:\Projects\Libs\Simplify\src\Simplify.WindowsServices.IntegrationTests\bin\Debug\Simplify.WindowsServices.IntegrationTests.exe install

Uninstallation example

D:\Projects\Libs\Simplify\src\Simplify.WindowsServices.IntegrationTests\bin\Debug\Simplify.WindowsServices.IntegrationTests.exe uninstall

Windows-service installer base class

ServiceInstallerBase class is allows you to install a windows service with information from your assembly information fields, like Title, Description.

Description and Title fileds of a specified assembly will be used as your service name and description.

  • Title will be used as a service ID
  • Description will be used as s service name and description (you will be able to see that information in Services.msc after service will be installed).

Default ServiceInstallerBase usage

[RunInstaller(true)]
public class ServiceInstaller : ServiceInstallerBase
{
}

Specifying service "RunAs" user

You can specify via configuration file user name and user password under which the service will be run.

<?xml version="1.0"?>

<configuration>
     <configSections>
        <section name="ServiceInstallerSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>

    <ServiceInstallerSettings>
        <add key="RunAsUserName" value="UserName"/>
        <add key="RunAsUserPassword" value="UserPassword"/>
    </ServiceInstallerSettings>
</configuration>

Specifying service system account

You can specify the service system account under which the service will be run

<ServiceInstallerSettings>
        <add key="ServiceAccount" value="NetworkService"/>
</ServiceInstallerSettings>

Possible values: LocalService, NetworkService, LocalSystem

Note: if RunAsUserName and RunAsUserPassword is set in the config, then the ServiceAccount parameter will be ignored.

Note: you can specify any user, for example: domain user as "YourDomain\UserName".

Additional settings

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="ServiceSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>

    <ServiceSettings>
        <!-- Execute GC.Collect(); after each "Run" method execution (default value is: true) -->
        <add key="CleanupOnTaskFinish" value="true" />

        <!-- The maximum allowed paraller tasks of the same job (the new task will be create in paraller if previous task is not ended execution). (default value is: 1, so the only one task of the same job can exist at a time) -->
        <add key="MaximumParallerTasksCount" value="2" />
    </ServiceSettings>
</configuration>

Microsoft.Extensions.Configuration IConfiguration settings support in Simplify.WindowsServices

Simplify.WindowsServices support configuration via IConfiguration instead of old ConfigurationManager App.config, just use one of the respective handler methods

Example

Program.cs

...
var configuration = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", false)
    .Build();

var handler = new SingleTaskServiceHandler<MyClass>(configuration);
...

appsettings.json

{
    "ServiceSettings":
    {
        "CrontabExpression": "* * * * *"
    }
}

Global exceptions catching which can be thrown when resolving instances by Simplify.DI in handler

Simplify.WindowsService cah catch all exceptions thown by a user code To receive such catch event you should subscribe to hander OnException event

static void Main(string[] args)
{
    var handler = new SingleTaskServiceHandler<MyClass>();

    handler.OnException += OnException;
    hander.Start(args);
}

static void OnException(ServiceExceptionArgs args)
{
    Console.Write(args.ServiceName);
    Console.Write(args.Exception.Message);
}

DI example

Classes

public interface IMyClass2
{
    SomeMethod();
}

public class MyClass2 : IMyClass2
{
    public SomeMethod()
    {
    }
}

public class MyClass
{
    private IMyClass2 _myclass2;

    public MyClass(IMyClass2 myclass2)
    {
        _myclass2 = myclass2;
    }

    public void Run()
    {
        _myclass2.SomeMethod();
    }
}

Program.cs

static void Main(string[] args)
{
    DIContainer.Current.Register<IMyClass2, MyClass2>();
    DIContainer.Current.Register<MyClass>();

    new SingleTaskServiceHandler<MyClass>().Start(args);
}

Clone this wiki locally