Ausnahme gefangen: SSL certificate problem: certificate is not yet valid 📌 .NET Console Application with injectable commands

🏠 Team IT Security News

TSecurity.de ist eine Online-Plattform, die sich auf die Bereitstellung von Informationen,alle 15 Minuten neuste Nachrichten, Bildungsressourcen und Dienstleistungen rund um das Thema IT-Sicherheit spezialisiert hat.
Ob es sich um aktuelle Nachrichten, Fachartikel, Blogbeiträge, Webinare, Tutorials, oder Tipps & Tricks handelt, TSecurity.de bietet seinen Nutzern einen umfassenden Überblick über die wichtigsten Aspekte der IT-Sicherheit in einer sich ständig verändernden digitalen Welt.

16.12.2023 - TIP: Wer den Cookie Consent Banner akzeptiert, kann z.B. von Englisch nach Deutsch übersetzen, erst Englisch auswählen dann wieder Deutsch!

Google Android Playstore Download Button für Team IT Security



📚 .NET Console Application with injectable commands


💡 Newskategorie: Programmierung
🔗 Quelle: dev.to

Console applications are alive and kicking. Setting them up might be a bit hard. In this article I'll explore how to create a .NET console application that provides commands using the new System.CommandLine package. This will provide arguments to command mapping out of the box. I'll be showing how to combine it with dependency injection for even more power ⚡.

  1. Goals
  2. NuGet Packages
  3. Project structure
  4. Fake weather service
  5. Commands
    1. Current Temperature Command
    2. Forecast Command
  6. Dependency injection
  7. Final thoughts

Goals

We want to create a CLI application with the following goals:

  • System.CommandLine — this is a fairly new project by .NET that helps to create better CLI applications. It offers the ability to add commands, arguments and options to your application. It comes with a --help feature and it will do the command line argument mapping for you.
  • Dependency Injection — why go anywhere without it? Dependency injection has made ASP.NET way more composable. I wrote an entire article on how to add it to console applications as well. We'll be reusing some of the code.
  • Environment variable injection support — some of the configuration should be overridable using environment variables.

We're making a CLI, so what's a better way to describe it than showing what the --help should look like?

Description:
  Weather information using a fake weather service.

Usage:
  MyCli [command] [options]

Options:
  --version       Show version information
  -?, -h, --help  Show help and usage information

Commands:
  current   Gets the current temperature.
  forecast  Get the forecast. Almost always wrong.

Note: if you want to use command line argument when executing a dotnet run, you can use -- to feed the arguments to the application instead of the .NET CLI (so dotnet run -- --help in this case).

NuGet Packages

If you say .NET, you say NuGet packages. We'll be using the following packages:

Install-Package System.CommandLine -Version 2.0.0-beta4.22272.1  
Install-Package Microsoft.Extensions.Configuration -Version 7.0.0  
Install-Package Microsoft.Extensions.Configuration.EnvironmentVariables -Version 7.0.0  
Install-Package Microsoft.Extensions.DependencyInjection -Version 7.0.0  
Install-Package Microsoft.Extensions.DependencyInjection.Abstractions -Version 7.0.0  
Install-Package Microsoft.Extensions.Options -Version 7.0.1  
Install-Package Microsoft.Extensions.Options.ConfigurationExtensions -Version 7.0.0

The System.CommandLine package is still in beta. I expect it to be released soon, but things might still change.

Project structure

I'm using the following project structure:

.
├── src/
│   └── MyCli/
│       ├── Commands/
│       │   ├── CurrentCommand.cs
│       │   └── ForcastCommand.cs
│       ├── Services/
│       │   ├── FakeWeatherService.cs
│       │   └── FakeWeatherServiceSettings.cs
│       └── Program.cs
└── MyCli.sln

Fake weather service

What is injection without a good service? Let's create a fake weather service that returns the temperature based on a randomizer:

namespace MyCli.Services;

public class FakeWeatherServiceSettings
{
    public string DefaultCity { get; set; } = "Zwolle, NLD";

    public int DefaultForecastDays { get; set; } = 5;
}

public class FakeWeatherService
{
    public FakeWeatherService(IOptions<FakeWeatherServiceSettings> settings)
    {
        Settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings));
    }

    public FakeWeatherServiceSettings Settings { get; }

    public Task<string> GetTemperature(string? city = null)
    {
        if (city == null) city = Settings.DefaultCity;

        var report = $"In {city} it is now {Random.Shared.Next(-20, 40)} degrees celcius.";
        return Task.FromResult(report);
    }

    public Task<string[]> Forecast(int days, string? city = null)
    {
        if (city == null) city = Settings.DefaultCity;

        var reports = new List<string>
        {
            $"Report for {city} for the next {days} days:"
        };

        for (var i = 0; i<days; i++)
        {
            var date = DateTime.Now.AddDays(i + 1).ToString("yyyy-MM-dd");
            var report = $"- {date}: {Random.Shared.Next(-20, 40),3} degrees celcius.";
            reports.Add(report);
        }

        return Task.FromResult(reports.ToArray());
    }
}

Commands

Commands are implementations of the System.CommandLine.Command class. To make them injectable, we create classes that are derived from the Command class (see dependency injection section).

Current Temperature Command

To get our current temperature command, we'll need to do the following:

  • Call the base constructor with the name and description of the command. This will be used by the --help feature.
  • Inject the FakeWeatherService, as it does the actual work.
  • Use the FakeWeatherService.Settings to get the default value for the --city option.
  • Map it all together using a SetHandler. The option in automatically mapped to the city parameter of the Execute method.

Now the implementation is very easy:

using MyCli.Services;
using System.CommandLine;

namespace MyCli.Commands;

class CurrentCommand : Command
{
    private readonly FakeWeatherService _weather;

    public CurrentCommand(FakeWeatherService weather) : base("current", "Gets the current temperature.")
    {
        _weather = weather ?? throw new ArgumentNullException(nameof(weather));

        var cityOption = new Option<string>("--city", () => _weather.Settings.DefaultCity, "The city.");

        AddOption(cityOption);

        this.SetHandler(Execute, cityOption);
    }

    private async Task Execute(string city)
    {
        var report = await _weather.GetTemperature(city);
        Console.WriteLine(report);
    }
}

What I like about the setup is that we can add optional arguments with defaults. Here we get the default value from an object from our dependency injection. When we do a current --help, we can a nice description and the actual injected value:

Description:
  Gets the current temperature.

Usage:
  MyCli current [options]

Options:
  --city <city>   The city. [default: Amsterdam, NLD]
  -?, -h, --help  Show help and usage information

Forecast Command

The same goes for the forecast command, but now we have 2 options: --city and --days.

using Microsoft.Extensions.Options;
using MyCli.Services;
using System.CommandLine;

namespace MyCli.Commands;

class ForecastCommand : Command
{
    private readonly FakeWeatherService _weather;

    public ForecastCommand(FakeWeatherService weather) : base("forecast", "Get the forecast. Almost always wrong.")
    {
        _weather = weather ?? throw new ArgumentNullException(nameof(weather));

        var cityOption = new Option<string>("--city", ()=> _weather.Settings.DefaultCity, "The city.");
        var daysOption = new Option<int>("--days", () => _weather.Settings.DefaultForecastDays, "Number of days.");

        AddOption(cityOption);
        AddOption(daysOption);

        this.SetHandler(Execute, cityOption, daysOption);
    }

    private async Task Execute(string city, int days)
    {
        var report = await _weather.Forecast(days, city);
        foreach (var item in report)
        {
            Console.WriteLine(item);
        }
    }
}

Dependency injection

Now, let's tie it all together using dependency injection. We need to do the following:

  • Setup a ServiceCollection to store our dependencies.
  • Setup the configuration to use environment variables and read them into our WeatherServiceSettings object.
  • Add the commands CurrentCommand and ForecastCommand to the service collection.
  • Add the WeatherService to the service collection.
  • Create a System.CommandLine.RootCommand and tie it to the registered Command implementation.
  • Invoke the root command with the given command line arguments.

This leads to the followingProgram.cs code:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MyCli.Commands;
using MyCli.Services;
using System.CommandLine;

static void ConfigureServices(IServiceCollection services)
{
    // build config
    var configuration = new ConfigurationBuilder()
        .AddEnvironmentVariables()
        .Build();

    // settings
    services.Configure<FakeWeatherServiceSettings>(configuration.GetSection("Weather"));

    // add commands:
    services.AddTransient<Command, CurrentCommand>();
    services.AddTransient<Command, ForecastCommand>();

    // add services:
    services.AddTransient<FakeWeatherService>();
}

// create service collection
var services = new ServiceCollection();
ConfigureServices(services);

// create service provider
using var serviceProvider = services.BuildServiceProvider();

// entry to run app
var commands = serviceProvider.GetServices<Command>();
var rootCommand = new RootCommand("Weather information using a fake weather service.");
commands.ToList().ForEach(command => rootCommand.AddCommand(command));

await rootCommand.InvokeAsync(args);

To make dependency injection work, we do a GetServices to retrieve all the commands and add them to the root command.

Final thoughts

And that's all: now you have a CLI that supports commands and a --help feature out of the box!

I've added the code to GitHub, so check it out: github.com/KeesCBakker/dotnet-cli-di-poc

...



📌 .NET Console Application with injectable commands


📈 66.38 Punkte

📌 Researchers Build Tiny Wireless, Injectable Chips, Visible Only Under a Microscope


📈 33.25 Punkte

📌 Mastering Injectable Services: A Comprehensive Guide


📈 33.25 Punkte

📌 Microsoft .NET Maze: Understand .NET Core Vs .NET Framework Vs ASP.NET


📈 25.18 Punkte

📌 Revitalizing Legacy .NET Apps: Upgrading From .NET 4.7 to .NET 7 With the .NET Upgrade Assistant Tool


📈 25.18 Punkte

📌 Qmail up to 1.0.3 on 64-bit commands.c commands memory corruption


📈 22.88 Punkte

📌 Complete Unix Commands And Basic Linux Commands With Examples For Beginners


📈 22.88 Punkte

📌 Unix Commands Cheat Sheet: All the Commands You Need


📈 22.88 Punkte

📌 SharpRDP - Remote Desktop Protocol .NET Console Application For Authenticated Command Execution


📈 21.69 Punkte

📌 Microsoft Releases a &quot;Windows Command Reference&quot; For Over 250 Console Commands


📈 20.69 Punkte

📌 Top 10 Best Minecraft Console Commands and Cheats 2019


📈 20.69 Punkte

📌 Announcing .NET Interactive &#8211; Try .NET includes .NET Notebooks and more


📈 18.88 Punkte

📌 What is the difference between NET Core, NET 5 and NET Framework? | One Dev Question


📈 18.88 Punkte

📌 Microsoft baut an neuem Upgrade-Assistent von .NET Framework zu .NET 5 und .NET 6


📈 18.88 Punkte

📌 Live Now: .NET Community Standup - May 16th 2019 - Build 2019 Recap for .NET Developers | .NET Community Standups


📈 18.88 Punkte

📌 .NET MAUI Community Standup - .NET Conf Recap and .NET MAUI Updates


📈 18.88 Punkte

📌 CVE-2016-2293 | Accuenergy Acuvim II NET/Acuvim IIR NET 3.08 AXM-NET Module Config access control


📈 18.88 Punkte

📌 Accuenergy Acuvim II NET/Acuvim IIR NET 3.08 AXM-NET Module Config Information Disclosure


📈 18.88 Punkte

📌 .NET Core and .NET Framework - what to choose? [1 of 3] | Desktop and .NET Core 101


📈 18.88 Punkte

📌 Accuenergy Acuvim II NET/Acuvim IIR NET 3.08 AXM-NET Module Password Information Disclosure


📈 18.88 Punkte

📌 Supporting VB.NET in .NET 5 | On .NET


📈 18.88 Punkte

📌 Accuenergy Acuvim II NET/Acuvim IIR NET 3.08 AXM-NET Module Config Information Disclosure


📈 18.88 Punkte

📌 Accuenergy Acuvim II NET/Acuvim IIR NET 3.08 AXM-NET Module Password Information Disclosure


📈 18.88 Punkte

📌 ML.NET and Model Builder at .NET Conf 2019 (Machine Learning for .NET)


📈 18.88 Punkte

📌 NeoEngine 0.8.2 Console console::render memory corruption


📈 18.51 Punkte

📌 Apple Safari 2.0.4 419.3 window.console.log() window.console.log denial of service


📈 18.51 Punkte

📌 webMethods Glue 4.0/5.0/6.5.1 Management Console console resource directory traversal


📈 18.51 Punkte

📌 Intro to Google Search Console - Search Console Training


📈 18.51 Punkte

📌 Performance reports in Search Console - Google Search Console Training


📈 18.51 Punkte

📌 Monitoring Rich Results in Search Console - Google Search Console Training


📈 18.51 Punkte

📌 console-io up to 2.2.13 on Node.js Web Console weak authentication


📈 18.51 Punkte











matomo