Custom command lines
CLI commands uses appsettings.Production.json so pay attention to this we developing.
Structure
- Folder with all commands. One file per command.
- CustomCommands.csproj file to load packages and include the main QuebecMunicipal project.
- Program.cs file is where the app starts. It configure the app, database connection and services required for a command line project.
We can use dependency injection in this project. While the web api is running, we can execute the commands without to stop the server. We reuse the same codebase (services, entities, etc.) and DbContext.
With options command example
public class ExampleOptions
{
public string PersonToGreet { get; set; }
}
public class WithOptionsCommand : Command
{
public WithOptionsCommand() : base("with-options-command")
{
AddOption(new Option<string>("--personToGreet", "Person to greet"));
}
}
public class WithOptionsCommandHandler : ICommandHandler
{
private readonly ExampleOptions _options;
public WithOptionsCommandHandler(IOptions<ExampleOptions> options)
{
_options = options.Value;
}
public int Invoke(InvocationContext context)
{
Console.WriteLine("Hello: " + _options.PersonToGreet);
return 0;
}
public Task<int> InvokeAsync(InvocationContext context)
{
return Task.FromResult(Invoke(context));
}
}
dotnet run --project ".\CLI\CustomCommands.csproj" -- with-options-command --personToGreet John
Without option command example
public class WithoutOptionCommand : Command
{
public WithoutOptionCommand() : base("without-option-command") { }
}
public class WithoutOptionCommandHandler : ICommandHandler
{
public WithoutOptionCommandHandler() { }
public int Invoke(InvocationContext context)
{
Console.WriteLine("Command without option executed.");
return 0;
}
public Task<int> InvokeAsync(InvocationContext context)
{
return Task.FromResult(Invoke(context));
}
}
dotnet run --project ".\CLI\CustomCommands.csproj" -- without-option-command
Program.cs
internal class Program
{
private static async Task<int> Main(string[] args)
{
var parser = BuildCommandLine()
.UseHost(_ => Host.CreateDefaultBuilder(args), builder => builder
.ConfigureServices((hostBuilderContext, services) =>
{
IConfiguration configuration = CreateConfiguration(hostBuilderContext);
AppSettingsHelper.Configure(configuration);
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.Configure<ExampleOptions>(configuration);
Startup.RegisterDbContextForDependencyInjection(configuration, services);
Startup.RegisterServicesForDependencyInjection(services);
})
.UseCommandHandler<WithOptionsCommand, WithOptionsCommandHandler>()
.UseCommandHandler<WithoutOptionCommand, WithoutOptionCommandHandler>())
.UseDefaults()
.Build();
return await parser.InvokeAsync(args);
}
private static CommandLineBuilder BuildCommandLine()
{
var rootCommand = new RootCommand();
rootCommand.AddCommand(new WithOptionsCommand());
rootCommand.AddCommand(new WithoutOptionCommand());
return new CommandLineBuilder(rootCommand);
}
private static IConfiguration CreateConfiguration(HostBuilderContext hostBuilderContext)
{
// TODO: multiple environnement (Development and Production) with command line project?
IHostEnvironment hostEnvironment = hostBuilderContext.HostingEnvironment;
string appSettingsFilename = $"appsettings.{hostEnvironment.EnvironmentName}.json";
string currentPath = Path.GetFullPath(Path.Combine(@"../"));
Environment.CurrentDirectory = currentPath;
string settingPath = Path.Combine(currentPath, appSettingsFilename);
var builder = new ConfigurationBuilder()
.AddConfiguration(hostBuilderContext.Configuration)
.AddJsonFile(settingPath);
return builder.Build();
}
}
If commands cannot be executed at the same time as the server is running
If the command cannot be executed at the same time as the server is running and/or that you see this error :
It means that the build files have been generated in the wrong order, which is normal during development but should not cause any problems in production. Stop the server, delete the following folders :
.
References
- ⭐ Multiple Projects with .Net Core and Visual Studio Code
- ⭐ Write a modern .Net Console application
- Read settings json file from the parent directory
- Tutorial: Get started with System.CommandLine
- Getting a compile time error CS0579: Duplicate 'AssemblyFileVersionAttribute' attribute
- SystemCommandLine.Demo
- ASP.NET Core configuration for .NET Core console application