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

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



