برنامه نویسی

برنامه کنسول دات نت با دستورات تزریقی

برنامه های کنسول زنده و پرکار هستند. تنظیم آنها ممکن است کمی سخت باشد. در این مقاله نحوه ایجاد یک برنامه کنسول دات نت که دستورات را با استفاده از نرم افزار جدید ارائه می کند را بررسی می کنم System.CommandLine بسته بندی این آرگومان هایی را برای نگاشت دستور خارج از جعبه ارائه می دهد. من نحوه ترکیب آن را با تزریق وابستگی برای قدرت بیشتر نشان خواهم داد ⚡.

  1. اهداف
  2. بسته های NuGet
  3. ساختار پروژه
  4. سرویس هواشناسی تقلبی
  5. دستورات

    1. فرمان دمای فعلی

    2. فرمان پیش بینی
  6. تزریق وابستگی
  7. افکار نهایی

ما می خواهیم یک برنامه CLI با اهداف زیر ایجاد کنیم:

  • System.CommandLine – این یک پروژه نسبتاً جدید توسط دات نت است که به ایجاد برنامه های بهتر CLI کمک می کند. قابلیت اضافه کردن را ارائه می دهد دستورات، استدلال ها و گزینه ها به درخواست شما همراه با الف است --help ویژگی و نقشه آرگومان خط فرمان را برای شما انجام می دهد.
  • تزریق وابستگی – چرا بدون اون جایی بری؟ تزریق وابستگی، ASP.NET را به شکلی قابل‌ترکیب‌تر ساخته است. من یک مقاله کامل در مورد نحوه اضافه کردن آن به برنامه های کنسول نیز نوشتم. برخی از کدها را دوباره استفاده خواهیم کرد.
  • پشتیبانی از تزریق متغیر محیطی – برخی از پیکربندی ها باید با استفاده از متغیرهای محیطی قابل لغو باشد.

ما در حال ساختن یک CLI هستیم، بنابراین چه راهی بهتر از نشان دادن آن چیست --help باید شبیه؟

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.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه: اگر می خواهید هنگام اجرای a از آرگومان خط فرمان استفاده کنید dotnet run، شما می توانید استفاده کنید -- برای تغذیه آرگومان ها به برنامه به جای CLI.NET (بنابراین dotnet run -- --help در این مورد).

اگر می گویید دات نت، می گویید بسته های NuGet. ما از بسته های زیر استفاده خواهیم کرد:

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
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این System.CommandLine بسته هنوز در نسخه بتا است. من انتظار دارم که به زودی منتشر شود، اما ممکن است اوضاع همچنان تغییر کند.

من از ساختار پروژه زیر استفاده می کنم:

.
├── src/
│   └── MyCli/
│       ├── Commands/
│       │   ├── CurrentCommand.cs
│       │   └── ForcastCommand.cs
│       ├── Services/
│       │   ├── FakeWeatherService.cs
│       │   └── FakeWeatherServiceSettings.cs
│       └── Program.cs
└── MyCli.sln
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تزریق بدون سرویس خوب چیست؟ بیایید یک را ایجاد کنیم سرویس هواشناسی تقلبی که دما را بر اساس تصادفی ساز برمی گرداند:

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());
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دستورات پیاده سازی از System.CommandLine.Command کلاس برای اینکه آنها را تزریق کنند، کلاس هایی ایجاد می کنیم که از the مشتق شده اند Command کلاس (به بخش تزریق وابستگی مراجعه کنید).

فرمان دمای فعلی

برای بدست آوردن ما دمای فعلی دستور، ما باید موارد زیر را انجام دهیم:

  • سازنده پایه را با نام و شرح از فرمان این مورد استفاده قرار خواهد گرفت --help ویژگی.
  • تزریق کنید FakeWeatherService، همانطور که کار واقعی را انجام می دهد.
  • استفاده کنید FakeWeatherService.Settings برای به دست آوردن مقدار پیش فرض برای --city گزینه.
  • همه آن را با هم با استفاده از a نقشه برداری کنید SetHandler. گزینه در به طور خودکار به نقشه برداری city پارامتر از Execute روش.

اکنون پیاده سازی بسیار آسان است:

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);
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چیزی که من در مورد تنظیمات دوست دارم این است که می توانیم اضافه کنیم آرگومان های اختیاری با پیش فرض ها. در اینجا ما مقدار پیش فرض را از یک شی از تزریق وابستگی خود دریافت می کنیم. وقتی ما یک current --help، ما می توانیم یک توصیف خوب و ارزش واقعی تزریق شده:

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 هم همینطور است، اما اکنون 2 گزینه داریم: --city و --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);
        }
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا بیایید همه آن ها را با استفاده از تزریق وابستگی به هم گره بزنیم. باید موارد زیر را انجام دهیم:

  • راه اندازی a ServiceCollection برای ذخیره وابستگی هایمان
  • پیکربندی را برای استفاده از متغیرهای محیطی تنظیم کنید و آنها را در ما بخوانید WeatherServiceSettings هدف – شی.
  • دستورات را اضافه کنید CurrentCommand و ForecastCommand به مجموعه خدمات
  • اضافه کردن WeatherService به مجموعه خدمات
  • ایجاد یک System.CommandLine.RootCommand و آن را به ثبت شده گره بزنید Command پیاده سازی.
  • دستور root را با آرگومان های خط فرمان داده شده فراخوانی کنید.

این منجر به موارد زیر می شودProgram.cs کد:

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);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اینکه تزریق وابستگی عمل کند، یک کار را انجام می دهیم GetServices برای بازیابی تمام دستورات و اضافه کردن آنها به دستور root.

و این همه است: اکنون یک CLI دارید که از دستورات و a پشتیبانی می کند --help ویژگی خارج از جعبه!

من کد را به GitHub اضافه کردم، بنابراین آن را بررسی کنید: github.com/KeesCBakker/dotnet-cli-di-poc

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا