1) You can listen for requests for your application to close without making your own loop, and without having to push the logic of your application into another task
var cts = new CancellationTokenSource();
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => cts.Cancel();
Console.CancelKeyPress += (sender, eventArgs) => { cts.Cancel(); eventArgs.Cancel = true; };
So you don't need Task.Run anymore. This is also a better, more consistent check for closing that is better implemented than anything you can hand roll in a main method.
(fyi. Unless you choose to leak these (and know what that means) you'll need to dispose cts and unregistered the handlers)
2) I almost certain passing a cancellation token to ExecuteReaderAsync is sufficient to cancel the query. You can verify this, but I think that cmd.Cancel for for people using ExecuteReader (non-async), which doesn't have a cancellationtoken.
3) Some other comments. It looks like you're not actually reading anything, is this code incomplete? A simple while loop is also probably not a great event loop (eg a delay would be good) but you don't need to worry about that anyway.