Custom command lines

CLI commands uses appsettings.Production.json so pay attention to this we developing.

Structure

Screenshot

  • 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 : Alt text

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 :

Alt text.


References

results matching ""

    No results matching ""