Создание сервиса

Последнее обновление: 16.11.2022

Интерфейс IHostedService

Сервис должен представлять реализацию интерфейса IHostedService, который определяет два метода:

  • StartAsync() – вызывается при запуске сервиса, обычно содержит код запуска выполняемой в рамках сервиса фоновой задачи

  • StopAsync() – вызывается, когда приложение завершает работу, и обычно содержит код завершения фоновой задачи и освобождения используемых ресурсов.

    Следует учитывать, что если приложение завершается аварийно, то метод StopAsync и соответственно его логика может не вызываться.

То есть если вкратце, мы создем сервис для выполнения какой-то задачи. В методе StartAsync запускаем эту задачу, а в StopAsync контроллируем ее завершение.

Класс BackgroundService

Также .NET предоставляет готовую реализацию этого интерфейса в виде абстрактного класса BackgroundService

public abstract class BackgroundService : IHostedService, IDisposable
{
    public virtual void Dispose();
    public virtual Task StartAsync(CancellationToken cancellationToken);
    public virtual Task StopAsync(CancellationToken cancellationToken);
    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
}

Он добавляет еще один метод - ExecuteAsync(), который запускает фоновый сервис:

protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

Этот метод запускается в методе StartAsync(). Таким образом, мы можем унаследовать класс сервиса от BackgroundService и всю логику сервиса определить в методе ExecuteAsync(), собственно как это и делается в сервисе Worker по умолчанию.

Пример реализации сервиса

Реализуем простейший сервис, который отслеживает изменения в определенной папке. Для этого определим следующую программу:

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<FileWatcherService>();
    })
    .Build();

host.Run();

public class FileWatcherService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var filePath = "folderEvents.txt";
        using var watcher = new FileSystemWatcher("D:\\Temp");

        // записываем изменения
        watcher.Changed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Changed: {e.FullPath}\n");
        // записываем данные о создании файлов и папок
        watcher.Created += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Created: {e.FullPath}\n");
        // записываем данные об удалении файлов и папок
        watcher.Deleted += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Deleted: {e.FullPath}\n");
        // записываем данные о переименовании
        watcher.Renamed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Renamed: {e.OldFullPath} to {e.FullPath}\n");
        // записываем данные об ошибках
        watcher.Error += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Error: {e.GetException().Message}\n");

        watcher.IncludeSubdirectories = true; // отслеживаем изменения в подкаталогах
        watcher.EnableRaisingEvents = true;    // включаем события

        // если операция не отменена, то выполняем задержку в 200 миллисекунд
        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(200, stoppingToken);
        }
        await Task.CompletedTask;
    }
}

Итак, здесь определен сервис FileWatcherService, который унаследован от BackgroundService. В методе ExecuteAsync создаем объект FileSystemWatcher, который будет отслеживать события в папке "D:\\Temp"

using var watcher = new FileSystemWatcher("D:\\Temp");

Далее прикрепляем обработчики к каждому из событий файловой системы, в которых записываем в файл соответствующую информацию:

 // записываем изменения
watcher.Changed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Changed: {e.FullPath}\n");
// записываем данные о создании файлов и папок
watcher.Created += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Created: {e.FullPath}\n");
// записываем данные об удалении файлов и папок
watcher.Deleted += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Deleted: {e.FullPath}\n");
// записываем данные о переименовании
watcher.Renamed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Renamed: {e.OldFullPath} to {e.FullPath}\n");
// записываем данные об ошибках
watcher.Error += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Error: {e.GetException().Message}\n");

Затем запускается цикл, который выполняется пока для переданного токена не поступит запрос отмены операции

while (!stoppingToken.IsCancellationRequested)
{
  await Task.Delay(200, stoppingToken);
}

Собственно пока выполняется цикл, будет идти отслеживание событий в папке.

И добавляем сервис FileWatcherService в коллекцию сервисов хоста:

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<FileWatcherService>();
    })
    .Build();

Запустим приложения и произведем в папке D:\\Temp различные действия с файлами - создание, удаление, переименование. В итоге в проекте появится текстовый файл "folderEvents.txt", который будет содержать строки типа:

16.11.2022 20:24:30 Created: D:\Temp\Текстовый документ.txt
16.11.2022 20:24:35 Renamed: D:\Temp\Текстовый документ.txt to D:\Temp\hello.txt
16.11.2022 20:24:39 Created: D:\Temp\Текстовый документ.txt
16.11.2022 20:24:42 Renamed: D:\Temp\Текстовый документ.txt to D:\Temp\test.txt
16.11.2022 20:24:46 Deleted: D:\Temp\hello.txt

Реализация с IHostedService

Пример аналогичного сервиса в виде реализации интерфейса IHostedService:

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<FileWatcherService2>();
    })
    .Build();

host.Run();

public class FileWatcherService2 : IHostedService
{
    FileSystemWatcher? watcher;
    string filePath = "folderEvents.txt";
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        watcher = new FileSystemWatcher("D:\\Temp");

        // записываем изменения
        watcher.Changed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Changed: {e.FullPath}\n");
        // записываем данные о создании файлов и папок
        watcher.Created += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Created: {e.FullPath}\n");
        // записываем данные об удалении файлов и папок
        watcher.Deleted += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Deleted: {e.FullPath}\n");
        // записываем данные о переименовании
        watcher.Renamed += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Renamed: {e.OldFullPath} to {e.FullPath}\n");
        // записываем данные об ошибках
        watcher.Error += async (o, e) => await File.AppendAllTextAsync(filePath, $"{DateTime.Now} Error: {e.GetException().Message}\n");

        watcher.IncludeSubdirectories = true; // отслеживаем изменения в подкаталогах
        watcher.EnableRaisingEvents = true;    // включаем события
        await Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        watcher?.Dispose();
        await Task.CompletedTask;
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850