Reputation: 53
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