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

برنامه های کنسول زنده و پرکار هستند. تنظیم آنها ممکن است کمی سخت باشد. در این مقاله نحوه ایجاد یک برنامه کنسول دات نت که دستورات را با استفاده از نرم افزار جدید ارائه می کند را بررسی می کنم System.CommandLine
بسته بندی این آرگومان هایی را برای نگاشت دستور خارج از جعبه ارائه می دهد. من نحوه ترکیب آن را با تزریق وابستگی برای قدرت بیشتر نشان خواهم داد ⚡.
- اهداف
- بسته های NuGet
- ساختار پروژه
- سرویس هواشناسی تقلبی
-
دستورات
-
فرمان دمای فعلی
- فرمان پیش بینی
-
- تزریق وابستگی
- افکار نهایی
ما می خواهیم یک برنامه 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