Open artiomchi opened 5 years ago
Can you give some code examples here showing the proposed usage?
Yes, we primarily work in master.
Sure,
The idea is to subclass the WindowsServiceLifetime
, and overtide the standard ServiceBase
methods, adding custom behaviour (making sure to not forget to call the base implementation on the start and stop methods).
For example:
public class CustomService : WindowsServiceLifetime
{
public CustomService(IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor)
: base(environment, applicationLifetime, loggerFactory, optionsAccessor)
{
// Enable whichever events you want to be notified of
CanPauseAndContinue = true;
CanShutdown = true;
CanHandleSessionChangeEvent = true;
CanHandlePowerEvent = true;
}
protected override void OnStart(String[] args)
{
base.OnStart(args);
// Custom start behaviour
}
protected override void OnPause()
{
base.OnPause();
// Service continue handler
}
protected override void OnContinue()
{
base.OnContinue();
// Service pause handler
}
protected override void OnShutdown()
{
base.OnShutdown();
// System shutdown handler
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
base.OnSessionChange(changeDescription);
// Session change handler
}
protected override void OnCustomCommand(Int32 command)
{
base.OnCustomCommand(command);
// Custom command handler
}
}
The service can also then be programatically paused/resumed, or sent custom commands to:
var serviceController = new ServiceController(ServiceName);
serviceController.ExecuteCommand(42);
serviceController.Pause();
I'll cherry pick the commit to the master branch then, and make a PR for a review
I forgot to add, to use this new class, it'd have to be registered when setting up the host:
Host.CreateDefaultBuilder(args)
.UseWindowsService<CustomService>()
Couldn't you do this today by calling services.AddSingleton<IHostLifetime, CustomService>();
?
Close, This should only be registered when running within the context of a web service. So the full solution becomes:
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
if (WindowsServiceHelpers.IsWindowsService())
services.AddSingleton<IHostLifetime, CustomService>();
})
I just thought to suggest a more streamlined option, as well as extend the documentation, so that making a windows service that can handle power/pause events is more obvious.
If this is considered unnecessary, then I'm happy to cancel the PR and close the issue
Just wanted to be clear on the benefits.
@glennc what do you think?
This makes sense but I'm not sure it would be a derived class, couldn't it just be a type that forwards those ServiceBase events?
Host.CreateDefaultBuilder(args)
.UseWindowsService<CustomServiceHandler>()
OR
Host.CreateDefaultBuilder(args)
.UseWindowsService(options => options.Events = new CustomServiceHandler());
Hey David,
Yeah, it could be done this way too. I've been playing with several ideas in my head, and settled on my implementation for the sake of simplicity, but it does have it's issues (like not calling base.OnStart(args)
method will hang the service).
Another thought I had was extending the IHostApplicationLifetime to add extra events, at least the pause/resume ones, but it's set up using CancellationToken
s, so that's not really an option, since they can be triggered multiple times. Plus they'd only work with a windows service anyway.
Your suggestion re: CustomServiceHandler
is a good one, but it'd most likely have to be the first implementation, since the handler might need access to other services to perform it's tasks, and should likely be injected by DI as a singleton. It could then handle standard service events for start/stop/pause/continue/shutdown/session events.
Should I then change my PR to something like this instead, adding a IWindowsServiceEventsHandler
interface?
As part of the migration of components from dotnet/extensions to dotnet/runtime (https://github.com/aspnet/Announcements/issues/411) we will be bulk closing some of the older issues. If you are still interested in having this issue addressed, just comment and the issue will be automatically reactivated (even if you aren't the author). When you do that, I'll page the team to come take a look. If you've moved on or workaround the issue and no longer need this change, just ignore this and the issue will be closed in 7 days.
If you know that the issue affects a package that has moved to a different repo, please consider re-opening the issue in that repo. If you're unsure, that's OK, someone from the team can help!
I see that PR https://github.com/dotnet/extensions/pull/2572 was closed as not adding value. Was that because there isn't analogous behavior with Unix daemons?
Paging @dotnet/extensions-migration ! This issue has been revived from staleness. Please take a look and route to the appropriate repository.
Tagging subscribers to this area: @eerhardt, @maryamariyan See info in area-owners.md if you want to be subscribed.
Author: | artiomchi |
---|---|
Assignees: | - |
Labels: | `area-Extensions-Hosting`, `untriaged` |
Milestone: | - |
@artiomchi - I have the same issue. I need the OnSessionChange() which not exists in worker service. Did you find a solution? I'm using .NET 5 Thank you
I don't think we should duplicate all the APIs of ServiceBase
in Windows Service Hosting's layer. It is straightforward to implement this yourself as illustrated above. Doing it that way, you have the full power of ServiceBase
without needing to add additional APIs.
See also the conversation here: https://github.com/dotnet/runtime/issues/75276#issuecomment-1258446954
@RachelXGanon - have you tried the approach outlined above? Derive a class from WindowsServiceLifetime
and register it as the IHostLifetime
service. That approach is also described at: https://github.com/dotnet/runtime/issues/75276#issuecomment-1242336776
@eerhardt I appreciate your quick response.
Does it mean that in case that I need a service with the ServiceBase
functionality, I can create custom service that derived from ServiceBase
, in console app project?
And I don't need the worker service template?
Thank you.
@RachelXGanon - you can make either approach work. If you don't want/need the functionality that the "worker service" template brings you (DependencyInjection, Logging, Configuration, etc), you can just create a regular Windows Service using ServiceBase
and a console app project. (or a Windows Service (.NET Framework) project that you upgrade to .NET 6/7.)
But if you want the functionality the worker service brings, you can make your own class derived from WindowsServiceLifetime
(which itself derives from ServiceBase
) as described above and override OnSessionChange()
.
On the topic of what this adds; I looked at the source for WindowsServiceLifetime and it doesn't implement OnPause
at all. The windows service control does have a pause button. What happens when an admin presses pause in the service control dialogue? Nothing?
@A9G-Data-Droid, because WindowsServiceLifetime does not set the ServiceBase.CanPauseAndContinue property, its value should remain false. The service control UI should detect that and disable the pause button.
Is your feature request related to a problem? Please describe.
Currently there's a simple way to run a netcore3 app as a windows service using the
.UseWindowsService()
extension. The issue is that this extension uses it's ownServiceBase
class, which is the only class that receives pause and continue events, shutdown events and custom service commands.Describe the solution you'd like
Add a second generic extension method that allows a custom class to be passed to
.UseWindowsService<>()
, which will be based on the existingWindowsServiceLifetime
, retaining all functionality, but allowing extra customisation.Describe alternatives you've considered
I've thought of altering the
WindowsServiceLifetime
class to receive aICollection<ServiceBase>
from DI, and register them alongside itself in theServiceBase.Run()
call, but that will mean theApplicationLifetime.ApplicationStarted
will be called only after theWindowsServiceLifetime
service has started, not when all services have.Also, it seems to make sense to only have one service registered, and move "service" functionality to hosted service classes instead.
Additional context
I've added an implementation to my fork, branched off
release/3.0
, since I wasn't sure which branch would be best to start the fix from: feature/windows-service-custom-class, with the fix being in this commit: d9bafd6If you let me know whether I should do it from
master
, or any other branch, I can make a PR for this.