【C#, WinForms】.NET 5.0 時代の構成ファイル
はじめに
C# デスクトップアプリにて、外部ファイルから構成を読み込みたい
構成:データベースへの接続文字列だとか、何らかの機能の初期値など
(ASP.NETでは、appsettings.jsonを使うらしい)
JSONファイルが登場する以前からある方法として、INIファイルを使う方法がある
(参考:2003年に書かれた atmarkIT の記事)
.NET Framework から .NET Core を経て、.NET5(現在) となった今、もっとモダンな方法がないか調べて実際に試してみた
試したときの状況
- Microsoft Visual Studio Community 2022 (64 ビット) - Preview
- Version 17.1.0 Preview 3.0
- Windows フォームアプリ
- .NET 5.0(現在)
INIファイルを使って構成を読み込む方法
これまで自分が使ってきた方法のおさらい
DLLImport
先述の記事の方法
謎のおまじない[DllImport("KERNEL32.DLL")]
が書かれたユーティリティクラスを用意して使う
ini-parser
NuGetからプロジェクトにインストールできる
README.mdを読めばすぐに使える、扱いやすいライブラリ
しかし、2017年が最終リリースのため、.NET Core 以降に対応しておらず・・・
Issue上部に更新が止まった経緯がピン止めされている
コロナ・転職・子ども、などライフイベントで時間がとれなくなったから、メンテナ募集します、とのこと。
互換性がないと表示されるエラーメッセージ
パッケージ 'ini-parser 2.5.2' はプロジェクトのターゲット フレームワーク '.NETStandard,Version=v2.1' ではなく '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' を使用して復元されました。このパッケージは、使用しているプロジェクトとの完全な互換性がない可能性があります。
ini-parser-standard
.NET Standard 2.0 に依存してビルドされたバージョンがある
こちらであれば .NET5 からもサポートされているので、プロジェクトにインストールできる
作者は本家ini-parserと同一で、プロジェクトURLはini-parserと同じだけど
README.mdで言及されていないし、なんかよくわからない代物
【本題】モダンな方法
MS公式ドキュメントにINI ファイル、JSON ファイル、XML ファイルなどに応じた方法が書かれている
INIファイルについて詳細に書かれているページはこちら
構成ファイルの利点について、わかりやすく書かれている
構成ファイルを利用すると、実行時に読み取られアプリケーションの動作に影響を与える、一連のプロパティを格納できます。たとえば、データベースを配置する場所や、ループを実行する回数などです。 この手法の利点は、コードの書き直しや再コンパイルを行わずに、アプリケーションの一部の側面を変更できることです。
そしてモダンな方法への切り替えを推奨している
.NET の世界に machine.config ファイルはありません。 また、旧式の System.Configuration 名前空間を使用し続けることもできますが、多くの拡張機能を備えたモダンな Microsoft.Extensions.Configuration に切り替えることを検討してください。
モダンな方法を実際に試してみる
実際にプロジェクトを作成し、順を追ってMicrosoft.Extensions.Configuration
を使った方法を試す
なお、すでに試されている記事もある(Qiita)(参考にさせていただきました)。
プロジェクトを作成する
.NET 5 フォームアプリのプロジェクトを作成し、必要なパッケージを NuGet からインストールする
Install-Package Microsoft.Extensions.Configuration.Ini
Install-Package Microsoft.Extensions.Configuration.Binder
Install-Package Microsoft.Extensions.Logging
パッケージ | 用途 |
---|---|
Configuration.Ini | INIファイルから構成を読みこむため |
Configuration.Binder | ConfigurationBinder 拡張メソッドを使用するため |
Logging | 今回あつかう構成ファイル中に"ログレベル"の項目が存在するため |
INIファイルを用意する
こちらの公式Docと同じINIファイルを使う
INIファイルを作成したら、exeファイルと同じ階層に配置する
(例)WinFormsApp1\bin\Debug\net5.0-windows\appsettings.ini
SecretKey="Secret key value"
[TransientFaultHandlingOptions]
Enabled=True
AutoRetryDelay="00:00:07"
[Logging:LogLevel]
Default=Information
Microsoft=Warning
POCOを作成する
INIファイルの各セクションに対応したオブジェクトを作成する
public class TransientFaultHandlingOptions
{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}
public class Logging
{
public const string SectionName = "Logging:LogLevel";
public LogLevel Default { get; set; }
public LogLevel Microsoft { get; set; }
}
ルートとなるBindingClass
クラス
public class BindingClass
{
public string SecretKey { get; set; }
public TransientFaultHandlingOptions TransientFaultHandlingOptions { get; set; }
public Logging Logging { get; set; }
}
INIファイルから構成を読み込む
INIファイルから構成を読み込んで、コンソール出力する
また、変数にバインディングする
using Microsoft.Extensions.Configuration;
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Extensions.Logging;
static void Main()
{
var builder = new ConfigurationBuilder()
.AddIniFile(path: "appsettings.ini");
var configuration = builder.Build();
// コンソール出力する
foreach ((string key, string value) in
configuration.AsEnumerable().Where(t => t.Value is not null))
{
Debug.WriteLine($"{key}={value}");
}
// 変数にバインディングする
BindingClass bind = configuration.Get<BindingClass>();
Logging logging = configuration.GetSection(Logging.SectionName).Get<Logging>();
}
コンソール出力
foreach
でキーと値のペアを出力した様子
TransientFaultHandlingOptions:Enabled=True
TransientFaultHandlingOptions:AutoRetryDelay=00:00:07
SecretKey=Secret key value
Logging:LogLevel:Microsoft=Warning
Logging:LogLevel:Default=Information
変数にバインディング
拡張メソッドをつかってバインディングしたbind
変数のLogging
プロパティにはうまくバインディングできていない
Logging
変数のDefault
とMicrosoft
には、デフォルト値のTrace
がセットされている
一方でGetSection(Logging.SectionName)
により個別で読み込んだlogging
変数には、うまくINIファイルの情報が読みこまれている
セクション名が[Logging:LogLevel]
のように:
で区切られていると、うまくバインディングできない
INIファイルに構成の変更を書きこ・・・めなかった
指定されたキーの構成値を設定するSet(string key, string value);
メソッドを見つけはしたものの、うまく保存できない
INIファイルの扱いについて書かれた公式ドキュメントが「保存」に言及していない時点で、書き込むことができない空気は感じていた・・・
GitHub「なぜConfigurationProviderにはセーブ機能がないのか?」
2016年のISSUEより回答を一部引用
- Configurationは基本的に
App.config
(これも読み取り専用)だよ。もし構成を書き換えたいのであれば、データベースとか使ったほうがいいと思うよ- Configuration is basically replacing App.config - which was read-only as well. In my opinion, if you need to write configuration you need some sort of database.
- ※ そのあとの返信で、
App.config
は書き換え可能と記載あり: https://stackoverflow.com/questions/11149556/app-config-change-value
- ※ そのあとの返信で、
- Configuration is basically replacing App.config - which was read-only as well. In my opinion, if you need to write configuration you need some sort of database.
.NET Core はもはやASP.NETだけのものじゃないのに、保存機能がないのは馬鹿げている・・など書かれている模様
おわりに
いずれにせよAPIの設計思想なんてつよつよエンジニアが決めることだから、私は与えられたもので凌ぐのみ
小さなデスクトップアプリをつくるにしても、なんらかのデータベースを使うことが求められているのかもしれない
(2022.02.26 追記) Settingをつかった構成保存
@Zuishin 様コメントをもとに本項目も追加
参考となる Qiita 記事 ↓↓
参考にさせていただいた記事
Author And Source
この問題について(【C#, WinForms】.NET 5.0 時代の構成ファイル), 我々は、より多くの情報をここで見つけました https://qiita.com/t13801206/items/e699da3efe614d014e9a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .