Ctrl+C and CancellationToken#
TLDR, How to enable#
Enable the feature with appRunner.UseCancellationHandlers()
or appRunner.UseDefaultMiddleware()
.
The problem space#
Console applications should stop gracefully when the user enters Ctrl+C
or Ctrl+Break
.
If your app is consuming the main thread the app will not exit right away.
Traditionally, this is solved with a following steps:
- Create a
CancellationTokenSource
and make thecancellationTokenSource.Token
available for the rest of the app to reference. - Subscribe to
Console.CancelKeyPress
and callcancellationTokenSource.Cancel()
when triggered.cancellationToken.IsCancellationRequested
will then return true. - Check
cancellationToken.IsCancellationRequested
in any looping code and pass the token to any libraries that check it. Instead ofThread.Sleep(...)
, usecancellationToken.WaitHandle.WaitOne(...)
orTask.Delay(..., cancellationToken)
Cancellation middleware#
When enabled, the framework will:
- set the
CommandContext.CancellationToken
with a new token. - register a parameter resolver for
CancellationToken
- cancel the token on
Console.CancelKepPress
AppDomain.CurrentDomain.ProcessExit
AppDomain.CurrentDomain.UnhandledException
whenUnhandledExceptionEventArgs.IsTerminating
== true
The framework checks the cancellation token before every step in the pipeline.
Using the CancellationToken#
The CancellationToken is easy to access in your commands thanks to parameter resolvers. Simply add a parameter to your command or interceptor method.
public void MigrateRecords(CancellationToken cancellationToken, List<int> ids)
{
foreach(int id in ids.TakeWhile(!cancellationToken.IsCancellationRequested))
{
MigrateRecord(id);
}
}
Tip
Remember to pass the CancellationToken to all database, web and service requests that take one.
Interactive sessions#
If you code a command to use an AppRunner to run another command, Console.CancelKepPress
will cancel the token for the newest CommandContext.
This enables cancelling long-running commands within an interactive session without cancelling the token for the command hosting the interactive session.
See the examples app with InteractiveSession.cs) and InteractiveMiddleware.cs) for an example of how to create an interactive session.
In the future, we hope to have a CommandDotNet.ReadLine package offering an interactive session with all the goodness that comes with ReadLine like autocomplete and history.