adarsh augustine
adarsh augustine

Reputation: 53

How to Implement OnCustomCommand() in .NET worker service which is configured to run as windows service

I am working on windows service using .NET worker. It implements BackgroundService class. The lifecycle of the service is maintained by ExecuteAsync(). I want to use OnCustomCommand() to call a method whenever service controller sends a custom command to this service. But BackgroundService class does not provide the required method.

I have gone through https://github.com/dotnet/runtime/issues/50021 and created a CustomService.cs class which implements WindowsServiceLifetime class then tried installing it, but seems like this class is not working at all. Thing is I am not sure of how to debug windows services if it is actually possible to debug with out installing the service. Also I want to know whether the configuration in program.cs is done correctly.

class Program
{
    static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                if (WindowsServiceHelpers.IsWindowsService())
                    services.AddSingleton<IHostLifetime, CustomService>();
                services.AddHostedService<WindowsBackgroundService>();
            })
            .ConfigureLogging((hostContext, logging) =>
            {
                logging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
                logging.AddEventLog();
            })
            .UseWindowsService();
}

Here is the WindowsBackgroundService class and everything inside is it working fine after installing as windows service.

public sealed class WindowsBackgroundService : BackgroundService
{
private readonly ILogger<WindowsBackgroundService> _logger;
    private readonly IConfiguration _configuration;
     public WindowsBackgroundService(ILogger<WindowsBackgroundService> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;  
    }
public override async Task StartAsync(CancellationToken stoppingToken)
    {
        try
        {
            apiSendTimer = new System.Timers.Timer();
            apiSendTimer.Elapsed += OnApiSendTimerElapsed;
            retrieveSpanTimer = new System.Timers.Timer();
            retrieveSpanTimer.Elapsed += OnRetrieveSpanTimerElapsed;
            SetRetrieveSpanTimer();
            while (!stoppingToken.IsCancellationRequested)
            {
            }
        }
        catch (OperationCanceledException ex)
        {
            EnsureDirectoryExists(ErrorlogFilePath);
            string logErrorMessage = ($"Error {DateTime.Now} ExecuteAsyn() cancelled : {ex.Message} ");
            File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{Message}", ex.Message);
            EnsureDirectoryExists(ErrorlogFilePath);
            string logErrorMessage = ($"Error {DateTime.Now} ExecuteAsyn() cancelled : {ex.Message} ");
            File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
            Environment.Exit(1);
        }
    }

This is CustomService.cs with which I tried to implement OnCustomCommad() but unable to get the expected outcome

namespace App.WindowsService
{
    internal class CustomService : WindowsServiceLifetime
    {
        private readonly WindowsBackgroundService _backgroundService;
        private readonly ILogger<CustomService> _logger;
        public event EventHandler CustomCommandEvent;
        private int cutsomCommand = 129;
        private string ErrorlogFilePath = "C:\\WindowsServicesTest\\WorkerService\\BgLogFileErrors.txt";
        public CustomService(WindowsBackgroundService backgroundService, IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor, ILogger<CustomService> logger)
            : base(environment, applicationLifetime, loggerFactory, optionsAccessor)
        {
            _backgroundService = backgroundService ?? throw new ArgumentNullException(nameof(backgroundService));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
        protected override void OnStart(String[] args)
        {
            base.OnStart(args);
            try
            {
               _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                string logMessage = $"Serivce Started at {DateTime.Now}";
                File.AppendAllText(ErrorlogFilePath, logMessage + Environment.NewLine);
            }
            catch (Exception ex)
            {
                _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                string logErrorMessage = ($"WIndows Service:  {DateTime.Now} Error in OnStart method: {ex.Message}");
                File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
            }
        }
        protected override void OnStop()
        {
            base.OnStop();
            try
            {
                _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                string logMessage = $"Serivce Stopped at {DateTime.Now}";
                File.AppendAllText(ErrorlogFilePath, logMessage + Environment.NewLine);
            }
            catch (Exception ex)
            {
                _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                string logErrorMessage = ($"WIndows Service:  {DateTime.Now} Error in OnStart method: {ex.Message}");
                File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
            }
        }
        /*The only values for a custom command that you can define in your application
         * or use in OnCustomCommand are those between 128 and 255.
         * Integers below 128 correspond to system-reserved values.*/
        protected override void OnCustomCommand(Int32 command)
        {
            base.OnCustomCommand(command);
            try
            {
                // Check if the custom command is 129
                if (command == this.cutsomCommand)
                {
                    _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                    string logErrorMessage = ($"Received custom command {DateTime.Now} ");
                    File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
                    _backgroundService.OnCustomCommandReceived();
                }
            }
            catch (Exception ex)
            {
                _backgroundService.EnsureDirectoryExists(ErrorlogFilePath);
                string logErrorMessage = ($"Error while getting/parsing timespan {DateTime.Now} - {ex.Message} ");
                File.AppendAllText(ErrorlogFilePath, logErrorMessage + Environment.NewLine);
            }
        }     
    }
}

The message Serivce Started at {DateTime.Now} is not logged which explain CustomService class is not executed. I want to know where I got this wrong, is it in the Host configuration or the class itself. Also I am new to .NET development, so kindly help me know if there is anything else which does not follows best practices.

Upvotes: 4

Views: 205

Answers (0)

Related Questions