ConsoleAppFramework を使ったコンソールアプリケーションの作り方
このドキュメントは、.NET ConsoleAppFramework を使用してコンソールアプリケーションを開発するためのガイドです。
最初のプロジェクト作成から、DI(依存性注入)、設定ファイル、ロギングの統合までを段階的に説明しています。
開発者がモダンなコンソールアプリケーションを効率的に構築できるよう、実用的なサンプルコードとともに解説しています。
ConsoleAppFramework の説明 https://github.com/Cysharp/ConsoleAppFramework
- ConsoleAppFramework を使ったコンソールアプリケーションの作り方
前提条件
- .NET 9.0
- ConsoleAppFramework 5.6
- DryIoc 6.2
最初のプロジェクトを作成する
プロジェクトを作成する
dotnet new console -n ConsoleApp1
以下の作業では ConsoleApp1 ディレクトリで作業するので移動する
cd ConsoleApp1
パッケージ ConsoleAppFramework をインストールする
dotnet add package ConsoleAppFramework
簡単なコマンドライン処理を実装する
Program.cs
using ConsoleAppFramework;
ConsoleApp.Run(args, (int x) =>
{
Console.WriteLine($"Hello World! {x}");
});
実行例
PS ConsoleApp1> dotnet run -- --x 10
Hello World! 10
コマンドクラスを自動登録して実行する
Commands/SampleCommand.cs
using ConsoleAppFramework;
namespace ConsoleAppFramework.Commands;
[RegisterCommands("sample")]
public class SampleCommand
{
[Command("run")]
public void Run(int x)
{
Console.WriteLine($"Hello World! {x}");
}
}
Program.cs
using ConsoleAppFramework;
var app = ConsoleApp.Create();
app.Run(args);
古いバージョンでは SampleCommand : ConsoleAppBase のように基底クラスに ConsoleAppBase とかを使っていたようだけど、現在のバージョンでは不要になっている。
実行と結果
PS ConsoleApp1> dotnet run -- sample run --x 12
Hello World! 12
[RegisterCommands("sample")] がコマンド、 [Command("run")] がサブコマンドのように登録される。
機能が一個しかないようなコンソールアプリケーションでは過剰かもしれないけど後々色々追加するならこのやり方は便利。
設定ファイル、ロギング、DI を使えるようにする
Generic Host などを使う場合は以下のようにして 設定ファイル、ロギング、DIの設定をしていた
var host = Host.CreateDefaultBuilder(args)
// Configurationの追加・上書き
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environment}.json", optional: true)
.AddEnvironmentVariables();
})
// Loggingの設定
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddNConsole();
logging.SetMinimumLevel(LogLevel.Information);
})
// DIコンテナにサービス登録
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IConfiguration>(hostContext.Configuration);
services.AddDbContext<SampleDbContext>(options =>
{
options.UseSqlite(hostContext.Configuration.GetConnectionString("default"));
options.UseLoggerFactory(new NLogLoggerFactory());
})
;
})
.Build();
もちろんこのようにして Generic Host を使ってもいいのだけど、このために Generic Host を使うのも少々過剰気味ではあるので ServiceCollection -> ServiceProvider を使う。
パッケージを追加する
DI、設定、ロギングの基本コンポーネントをインストールする。
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Extensions.Logging.Console
設定ファイルを追加する
appsettings.json
{
"SampleSettings": {
"Setting1": "Value1",
"Setting2": "Value2"
}
}
ビルド時に出力ディレクトリに最新fileをコピーするようにしておく
ConsoleApp1.csporj
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
DIによって注入されるサービスを実装する
サンプルのため、設定を受け取って出力するだけのサービス
using Microsoft.Extensions.Configuration;
namespace ConsoleApp1.Services;
public class SampleService
{
IConfiguration _configuration;
public SampleService(IConfiguration configuration)
{
_configuration = configuration;
}
public void Run()
{
var settingValue = _configuration["SampleSettings:Setting1"];
Console.WriteLine($"SampleService Run: Setting1={settingValue}");
}
}
コマンドがDI注入されたサービスを使えるようにする
コンストラクタで注入されたサービスを受け取るようにする。
using ConsoleApp1.Services;
using ConsoleAppFramework;
using Microsoft.Extensions.Logging;
namespace ConsoleAppFramework.Commands;
[RegisterCommands("sample")]
public class SampleCommand
{
ILogger<SampleCommand> _logger;
SampleService _service;
public SampleCommand(SampleService service, ILogger<SampleCommand> logger)
{
_logger = logger;
_logger.LogInformation("SampleCommand Constructor");
_service = service;
}
[Command("run")]
public void Run(int x)
{
_logger.LogInformation($"SampleCommand Run: {x}");
_service.Run();
_logger.LogInformation("SampleCommand Run End");
}
}
設定、ロギング、DIを構成する
using ConsoleAppFramework;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using ConsoleApp1.Services;
// 設定ファイルの読み込み
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
// DI、ロギングの設定
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(configuration)
.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddConsole();
})
;
// サービスの登録
services.AddTransient<SampleService>();
ConsoleApp.ServiceProvider = services.BuildServiceProvider();
var app = ConsoleApp.Create();
app.Run(args);
実行例
PS ConsoleApp1> dotnet run -- sample run --x 12
info: ConsoleAppFramework.Commands.SampleCommand[0]
SampleCommand Constructor
info: ConsoleAppFramework.Commands.SampleCommand[0]
SampleCommand Run: 12
info: ConsoleApp1.Services.SampleService[0]
SampleService Run: Setting1=Value1
info: ConsoleAppFramework.Commands.SampleCommand[0]
SampleCommand Run End
DryIoc と統合する
デコレーターやAOPを使いたいなら Microsoft.Extensions.DependencyInjection だけでは少々力不足なので DryIocなど外部のDIコンテナを使う。
DryIoc パッケージをインストールする
dotnet add package DryIoc.Microsoft.DependencyInjection
コンテナを構成する
using DryIoc.Microsoft.DependencyInjection;
// DryIoc コンテナの生成
var container = new DryIoc.Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
container.Register<SampleService>();
ConsoleApp が DryIoc のコンテナを使うようにする
ConsoleApp.ServiceProvider = container.BuildServiceProvider();
統合した結果の Program.cs
using ConsoleAppFramework;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using ConsoleApp1.Services;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
// 設定ファイルの読み込み
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
// DI、ロギングの設定
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(configuration)
.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddConsole();
})
;
// DryIoc コンテナの生成
var container = new DryIoc.Container(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithDependencyInjectionAdapter(services);
container.Register<SampleService>();
ConsoleApp.ServiceProvider = container.BuildServiceProvider();
var app = ConsoleApp.Create();
app.Run(args);
応用
-
Microsoft.Extensions.DependencyInjectionが使えるということは IServiceProvider が使えるDIコンテナが使えるということ。例えば DryIoc - コマンド実行フィルターはコマンドの実行前後に処理を挟める。例えばコマンド実行開始・終了のログなど。
その他
サンプルコード
ConsoleAppFramework のドキュメントに ConsoleApp.Create().ConfigureServices() みたいなサンプルが書かれていて、これをもとにDryIocとかはめ込めばいいじゃんとか思っていたらサービスも注入されないしアスペクトも使えないし何なのこれ、ConsoleApp.Services = container.BuildServiceProvider() みたいにしてもダメだし、とか思ってたけど単純に ConsoleAppBuilder.ConfigureService とか使わなくても済むことがだいぶたってから分かった。ドキュメントを読むべし、とは言うけどチェリーピックだとよくわからなくなる。