- 2022年5月19日
小西です。
自動化スクリプトなんかは PowerShell で書くことが多いのですが、.NET 6 の登場で「もうスクリプトも C# でよくね?」となったので布教のために少し書いてみます。
環境
- .NET 6+
- ConsoleAppFramework 4.1.0
プロジェクト作成
dotnet new コマンドを使用してコンソールアプリを作成します。コマンドを実行すると、実行ディレクトリ内に同名の .csproj
ファイルと Program.cs
ファイルが作成されます。
mkdir my-project
cd my-project
dotnet new console
ConsoleAppFramework の導入
このままでもいいですが、 ConsoleAppFramework を利用すると Generic Host の仕組みをコンソールアプリに適用できて便利なので入れておきます。
dotnet add package ConsoleAppFramework
ConsoleAppFramework を導入することにより、コマンドや引数を受け取るアプリを最小限のコードで実装することができます。
ConsoleApp.Run(args, () => Console.WriteLine("Hello, dotnet!"));
実行
dotnet run コマンドでアプリを実行できます。
dotnet run
複数のコマンド
1つのアプリで複数のコマンドを定義することもできます。
var app = ConsoleApp.Create(args);
app.AddCommand("hello", () =>
{
Console.WriteLine("Hello, dotnet!");
});
// 引数を受け取るコマンド
app.AddCommand("sleep", async ([Option("t")] int time) =>
{
await Task.Delay(TimeSpan.FromSeconds(time));
});
app.Run();
dotnet run コマンド実行時の --
以降の文字列はアプリへの引数として扱われるので、実行したいコマンドと引数を指定して実行します。
dotnet run -- sleep -t 3
DI
コマンド実行時に DI したい場合は ConsoleAppBase
から派生したクラスのコンストラクタで注入します。
AddCommand()
を使ったデリゲートによる定義時にも DI できるらしいのですが、なぜかうまく注入されませんでした。
using Microsoft.Extensions.Logging;
var app = ConsoleApp.Create(args);
// app.AddCommand("di", (ILogger logger) =>
// {
// // "ILogger が解決できず、\"Required parameter "logger" not found in argument.\" と怒られる。"
// // サンプルではコマンドの第一引数に ConsoleAppContext を受け取っているので真似してみたが変わらず。
// });
app.AddCommands<Commands>(); // Commands クラスのパブリックなメソッドがコマンドとして追加される。
app.Run();
public class Commands : ConsoleAppBase
{
private readonly ILogger<Commands> logger;
// コンストラクタでの注入
public Commands(ILogger<Commands> logger)
{
this.logger = logger;
}
public void Log()
{
logger.LogInformation("log log log");
}
}
自分で作ったクラスを注入する場合は ConsoleAppBuilder
を使用して DI を設定したビルダーから ConsoleApp
のインスタンスを作成します。
この辺が Generic Host に乗っかってることで統一的な書き方ができてサイコーですね。
using Microsoft.Extensions.DependencyInjection;
var builder = ConsoleApp.CreateBuilder(args);
// DI設定
builder.ConfigureServices(service =>
{
service.AddSingleton<MyService>();
});
var app = builder.Build();
app.AddCommands<Commands>();
app.Run();
public class Commands : ConsoleAppBase
{
private readonly MyService service;
public Commands(MyService service)
{
this.service = service;
}
public async Task Put()
{
await service.Execute();
}
}
public class MyService
{
public Task Execute()
{
Console.WriteLine("DI!!!");
return Task.CompletedTask;
}
}
まとめ
なんだか ConsoleAppFramework についての記事みたいになってしまいましたが、.NET 6 の Minimal API によるクラス定義の排除、ConsoleAppFramework による Generic Host サポートとコマンドのルーティングにより C# のコードをとてもシンプルに記述できるようになったことで、これまでは手軽さから PowerShell で書いていたスクリプトも C# で書くことでデバッグ性や高いパフォーマンスを得られるようになるのではないかと思います。