Heres a good trick for a loop.
For starters if you dont want the app to kill itself and keep it open its time to enter 2018 - Make us of the HostBuilder!
You can have the app startup and stayup and be async, win win win so as a quick example here is a Main entry point for my recent events engine:
public static async Task Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
var startup = new Startup();
var hostBuilder = new HostBuilder()
.ConfigureHostConfiguration(startup.ConfigureHostConfiguration)
.ConfigureAppConfiguration(startup.ConfigureAppConfiguration)
.ConfigureLogging(startup.ConfigureLogging)
.ConfigureServices(startup.ConfigureServices)
.Build();
await hostBuilder.RunAsync();
}
Add a nuget package ref to Microsoft.Extensions.Hosting.
You can skip all the startup... lines if you have now DI to sort out.
Also youd need to add this line to you project file:
<LangVersion>latest</LangVersion>
Once the app is running any implementation of IHostedService is run automatically.
I put my console code in here so here is my example of this:
using MassTransit;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Trabalhos.EventsEngine.Messages;
namespace Trabalhos.EventsEngine.ClientExample
{
public class SenderHostedService : IHostedService
{
private readonly IBusControl eventsEngine;
private readonly ILogger<SenderHostedService> logger;
public SenderHostedService(IBusControl eventsEngine, ILogger<SenderHostedService> logger)
{
this.eventsEngine = eventsEngine;
this.logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var products = new List<(string name, decimal price)>();
Console.WriteLine("Welcome to the Shop");
Console.WriteLine("Press Q key to exit");
Console.WriteLine("Press [0..9] key to order some products");
Console.WriteLine(string.Join(Environment.NewLine, Products.Select((x, i) => $"[{i}]: {x.name} @ {x.price:C}")));
for (;;)
{
var consoleKeyInfo = Console.ReadKey(true);
if (consoleKeyInfo.Key == ConsoleKey.Q)
{
break;
}
if (char.IsNumber(consoleKeyInfo.KeyChar))
{
var product = Products[(int)char.GetNumericValue(consoleKeyInfo.KeyChar)];
products.Add(product);
Console.WriteLine($"Added {product.name}");
}
if (consoleKeyInfo.Key == ConsoleKey.Enter)
{
await eventsEngine.Publish<IDummyRequest>(new
{
requestedData = products.Select(x => new { Name = x.name, Price = x.price }).ToList()
});
Console.WriteLine("Submitted Order");
products.Clear();
}
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private static readonly IReadOnlyList<(string name, decimal price)> Products = new List<(string, decimal)>
{
("Bread", 1.20m),
("Milk", 0.50m),
("Rice", 1m),
("Buttons", 0.9m),
("Pasta", 0.9m),
("Cereals", 1.6m),
("Chocolate", 2m),
("Noodles", 1m),
("Pie", 1m),
("Sandwich", 1m),
};
}
}
This will statup, run and stay running.
The cool thing here is also the for(;;) loop, this is a neat trick to make it run on a loop so you can re-do things over and over, the hosting thing just means you don't need to worry about keeping the console alive.
I even have an escape key like you want:
if (consoleKeyInfo.Key == ConsoleKey.Q)
{
break;
}
For an indepth example of using the Hosted setup, use this link https://jmezach.github.io/2017/10/29/having-fun-with-the-.net-core-generic-host/
Walks through it all so should help.