.NET core コンソールアプリを運用していますが
いつのまにか機能を詰め込みすぎて、全体が把握できなくなり
機能追加が困難な時期がやってきました。
そこで、ConsoleAppFrameworkをつかってコードを整理しようと思います。
機能ごとに別クラスにわけても起動時引数での制御が簡単になるので便利です。
ログなどは Microsoft.Extensions の仕組みを使うようです。
残念ながら Microsoft.Extensions 関連は素人なので組み合わせ方法を探すのに苦労しました。
ネットで情報漁って、なんとか形が見えてきたので、まとめておきます。
ConsoleAppFrameworkとは
ConsoleAppFramework - .NET Coreコンソールアプリ作成のためのマイクロフレームワーク(旧MicroBatchFramework)
慣れてしまうと普通のコンソールアプリには戻れませんね。
実装例
ますは結論からということでProgram.csです。
その後、それぞれの内容の解説をいれます。
usingSystem;usingSystem.IO;usingSystem.Threading.Tasks;usingConsoleAppFramework;usingMicrosoft.Extensions.Configuration;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingSerilog;namespaceConsoleAppFrameworkSampleApp{publicclassProgram{staticasyncTaskMain(string[]args){Log.Logger=CreateLogger();try{awaitCreateHostBuilder(args).RunConsoleAppFrameworkAsync(args);}catch(Exceptionex){Log.Fatal(ex,"Host terminated unexpectedly");}finally{Log.CloseAndFlush();}}publicstaticIHostBuilderCreateHostBuilder(string[]args)=>Host.CreateDefaultBuilder(args).UseSerilog().ConfigureServices((hostContext,services)=>{services.Configure<Settings>(hostContext.Configuration.GetSection("Settings"));});publicstaticILoggerCreateLogger()=>newLoggerConfiguration().ReadFrom.Configuration(CreateBuilder().Build()).Enrich.FromLogContext().WriteTo.Console().CreateLogger();publicstaticIConfigurationBuilderCreateBuilder()=>newConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json",optional:false,reloadOnChange:true).AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")??"Production"}.json",optional:true).AddEnvironmentVariables();}}
Install-Packageについて
ログはSerilogを使いますので、とりあえず以下のものをインストール
Install-PackageConsoleAppFrameworkInstall-PackageSerilog.Extensions.LoggingInstall-PackageSerilog.Extensions.HostingInstall-PackageSerilog.Settings.ConfigurationInstall-PackageSerilog.Sinks.ConsoleInstall-PackageSerilog.Sinks.RollingFileInstall-PackageSerilog.Enrichers.EnvironmentInstall-PackageSerilog.Enrichers.ProcessInstall-PackageSerilog.Enrichers.Thread
Main部分
ロガーを作成してConsoleAppFrameworkを起動します。
細かい設定は後述
staticasyncTaskMain(string[]args){Log.Logger=CreateLogger();try{awaitCreateHostBuilder(args).RunConsoleAppFrameworkAsync(args);}catch(Exceptionex){Log.Fatal(ex,"Host terminated unexpectedly");}finally{Log.CloseAndFlush();}}
ロガーの設定について
ロガーの設定は appsettings.json にて行うスタイルにします。
コードの中身はSerilog.Extensions.HostingのSampleの書き方を参考にしました。
publicstaticILoggerCreateLogger()=>newLoggerConfiguration().ReadFrom.Configuration(CreateBuilder().Build()).Enrich.FromLogContext().WriteTo.Console().CreateLogger();publicstaticIConfigurationBuilderCreateBuilder()=>newConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json",optional:false,reloadOnChange:true).AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")??"Production"}.json",optional:true).AddEnvironmentVariables();
余談ですが appsettings.jsonについて。
テスト環境で「appsettings.Develogment.json」
本番環境で「appsettings.Production.json」
を優先して読み込むとのこと
Microsoft.Extensions関連の設定について
Host.CreateDefaultBuilderで設定です
・Serilogの登録
・appsettings.jsonから設定情報を読み込む
(設定情報は以下のようなSettingクラスを用意するスタイルです)
DIで使いたいものができたら、ここでいろいろ増やせそうです。
publicstaticIHostBuilderCreateHostBuilder(string[]args)=>Host.CreateDefaultBuilder(args).UseSerilog().ConfigureServices((hostContext,services)=>{services.Configure<Settings>(hostContext.Configuration.GetSection("Settings"));});
publicclassSettings{publicstringPath{get;set;}publicstringConnectionString{get;set;}}
ConsoleAppBaseの実装例
上記のコードで準備が整いましたので
実際の処理部分の記述できます。
例として、ConsoleAppFrameworkのサンプルにあるHelloメソッド相当のものは以下の形になります。
publicclassBase1:ConsoleAppBase{readonlyILoggerlogger;readonlySettingssettings;publicBase1(ILogger<Base1>logger,IOptions<Settings>settingsOp){this.logger=logger;this.settings=settingsOp.Value;}publicvoidHello([Option("n","name of send user.")]stringname,[Option("r","repeat count.")]intrepeat=3){for(inti=0;i<repeat;i++){this.logger.LogInformation("Hello My ConsoleApp Base1 from {name} {path}",name,this.settings.Path);}}}
・コンストラクタでロガーと設定情報クラスを受け取ることができます。
(この辺りはMicrosoft.Extensions関連のお仕事)
DIがよくわからなかったのですが、コンストラクタで書いたものが勝手に降りてくると思ったらわかったつもりになりましたw
appsetting.jsonの例
設定クラスとSerilogの設定は以下の要領で
{"Settings":{"Path":"c:\\temp","ConnectionString":"(connection string)"},"Serilog":{"Using":["Serilog.Sinks.Console","Serilog.Sinks.RollingFile",],"MinimumLevel":"Verbose","WriteTo":[{"Name":"RollingFile","Args":{"pathFormat":"logs\\log-{Date}.txt","retainedFileCountLimit":"30"}},],"Enrich":["WithMachineName","WithProcessId","WithThreadId"],"Destructure":[{"Name":"ToMaximumDepth","Args":{"maximumDestructuringDepth":4}},{"Name":"ToMaximumStringLength","Args":{"maximumStringLength":100}},{"Name":"ToMaximumCollectionCount","Args":{"maximumCollectionCount":10}}]}}
起動方法
引数でConsoleAppBaseを切り替えて使います。
ConsoleAppFrameworkSampleApp.exe Base1.Hello -t TEST
これでログなどの共通機能はDIにあつめて、ConsoleAppBaseで個別機能を実装できるので
ConsoleAppBaseをばんばん増やしても安心ですね。
関連性の薄い機能は別々のConsoleAppBaseで書けば、
影響範囲を気にせずプログラムの機能追加ができますね。
余談
appsettings.jsonの実行時パス問題が発生した場合は以下参照
How can I get my .NET Core 3 single file app to find the appsettings.json file?
https://stackoverflow.com/questions/58307558/how-can-i-get-my-net-core-3-single-file-app-to-find-the-appsettings-json-file
config.SetBasePath(GetBasePath());config.AddJsonFile("appsettings.json",false);
privatestringGetBasePath(){usingvarprocessModule=Process.GetCurrentProcess().MainModule;returnPath.GetDirectoryName(processModule?.FileName);}