【C#】appsetting.jsonをコンソールで扱う

コンソールアプリでも appsettings.json が使いたい場合の設定と実装方法及びヘルパークラスの紹介です。

確認環境

この記事は以下環境で確認しています。

  • VS2019(16.11.4)
  • .NET 5(C#9.0)
  • コンソールプロジェクトが作成済み

パッケージの導入

「NuGet パッケージの管理」の「参照」から探してもいいですが、微妙にわかりにくかったので「パッケージマネージャー コンソール」からインストールします。

ツール > NuGet パッケージ マネージャー > パッケージ マネージャー コンソール

NuGetのページ: https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Json/

以下コマンドを入力します。

PM>
Install-Package Microsoft.Extensions.Configuration.Json -Version 5.0.0

プロジェクトに間接的に必要なDLLが以下の通り追加されます。

// 結構大量に追加されます
Microsoft.Extensions.Configuration.Abstractions.dll
Microsoft.Extensions.Configuration.dll
Microsoft.Extensions.Configuration.FileExtensions.dll
Microsoft.Extensions.Configuration.Json.dll
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileProviders.Physical.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Primitives.dll

定義ファイルの追加

ソリューションエクスプローラー > 対象プロジェクトのコンテキストメニュー > 追加 > 新しい項目

で「JavaScript JSON 構成ファイル」を「appsetting.json」という名前で追加します。(実際は名前は何でも良いです)

定義ファイルの編集

以下のように書けばアクセスできるようになります。

using System.IO;
using Microsoft.Extensions.Configuration;

internal class AppMain
{
    private static void Main(string[] args)
    {
        var configuration = 
            new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", true, true).Build();

        IConfigurationSection section = configuration.GetSection("appSettings");
        string str1 = section["Key1"];
        string str2 = section["Key2"];
        string str3 = section["Key3"];
    }
}

アクセス用のヘルパークラスの作成

上記のようなコードを書くのも面倒なのでヘルパークラスを作成して手間を軽減したいと思います。

AppSettingJsonクラス

以前の XML 形式の「アプリケーション 構成ファイル」と似たような形でアクセスできるといいので以下のような「appSettings」セクションの下に key-value 形式で値が並んでいる定義ファイルを想定してヘルパークラスを作成したいと思います。

// appsetting.json
{
    "appSettings": {
        "key1": "stringstring",
        "key2": 10,
        "key3": 123.5698
    }
}

実装は以下の通りです。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Configuration;

namespace Takap.Utility
{
    /// <summary>
    /// App.config と似たような形式で appsetting.json を扱えるようにします。
    /// </summary>
    public class AppSettingJson
    {
        #region 見本...
        // 
        // 見本:
        // 以下の通り記述されている appSettingsノードの中身に簡単にアクセスできるようにする 
        //
        // appsetting.json
        // {
        //     "appSettings": {
        //         "key1": "stringstring",
        //         "key2": 10,
        //         "key3": 123.5698
        //     }
        // }
        //
        #endregion

        // 固有の設定ファイルの名前
        private string filePath;
        // 設定値が格納されているセクションの名前
        private string srction;

        // 読み込んだ設定を保持する
        private static IConfiguration conf;

        //
        // Constructors
        // - - - - - - - - - - - - - - - - - - - -

        public AppSettingJson
        (
            string filePath = "appsettings.json", 
            string sectionName = "appSettings"
        )
        {
            var _f = filePath ?? throw new ArgumentNullException(nameof(filePath));
            var _s = sectionName ?? throw new ArgumentNullException(nameof(sectionName));

            this.filePath = _f;
            this.srction = _s;
        }

        //
        // Public Methods
        // - - - - - - - - - - - - - - - - - - - -

        /// <summary>
        /// appSetting セクション内の指定したキーに対応する値を取得します。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public string GetString(string key) => this.getValue(key);

        // 以下基本型として値を取得するメソッド

        public bool GetBool(string key) => bool.Parse(this.GetString(key));
        public byte GetByte(string key) => byte.Parse(this.GetString(key));
        public sbyte GetSByte(string key) => sbyte.Parse(this.GetString(key));
        public char GetChar(string key) => char.Parse(this.GetString(key));
        public decimal GetDecimal(string key) => decimal.Parse(this.GetString(key));
        public double GetDouble(string key) => double.Parse(this.GetString(key));
        public float GetFloat(string key) => float.Parse(this.GetString(key));
        public int GetInt(string key) => int.Parse(this.GetString(key));
        public uint GetUInt(string key) => uint.Parse(this.GetString(key));
        public long GetLong(string key) => long.Parse(this.GetString(key));
        public ulong GetULong(string key) => ulong.Parse(this.GetString(key));
        public short GetShort(string key) => short.Parse(this.GetString(key));
        public ushort GetUShort(string key) => ushort.Parse(this.GetString(key));

        /// <summary>
        /// 設定値を指定したフォーマットで <see cref="DateTime"/> 型に変換して取得します。
        /// </summary>
        public DateTime GetDateTime(string key, string format)
        {
            return DateTime.ParseExact(this.GetString(key),
                format, CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// 指定値を <see cref="TimeSpan"/> として取得します。変換は conv の処理を使用します。
        /// </summary>
        public TimeSpan GetTimeSpan(string key, Func<double, TimeSpan> conv)
        {
            return conv(this.GetDouble(key));
        }

        //
        // Not-Public Methods
        // - - - - - - - - - - - - - - - - - - - -

        // 定義が読み込まれていなければ読み込む
        private void init()
        {
            if (conf == null)
            {
                conf = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile(this.filePath, true, true)
                    .Build();
            }
        }

        // 対象のセクションの値を取得する
        private string getValue(string key)
        {
            this.init();

            IConfigurationSection sec =
                conf.GetSection(this.srction) ??
                throw new KeyNotFoundException($"{this.srction} section is not found.");

            return sec[key] ?? throw new KeyNotFoundException($"Key not found. key={key}");
        }
    }
}

コンストラクタで指定した単一のセクションのみを対象に読み書きを単純化できるような実装にしています。値の取得を GetValur としてもよかったのですが微妙にパフォーマンスが悪かったので各基本型ごとの値の取得メソッドを専用に作成しています。

使い方

使い方ですが以下の通りです。

class Program
{
    static void Main(string[] args)
    {
        // {
        //     "appSettings": {
        //         "key1": "stringstring",
        //         "key2": 10,
        //         "key3": 123.5698
        //     }
        // }
        //
        // 既定だと "appsetting.json" の appSettings を読みに行く
        //
        AppSettingJson setting = new();
        var a = setting.GetString("key1");
        var b = setting.GetInt("key2");
        var c = setting.GetFloat("key3");
        var d = setting.GetFloat("key4"); // KeyNotFoundException


        // {
        //     "customSection": {
        //         "key1111": "stringstring",
        //         "key2222": 10,
        //         "key3333": 123.5698
        //     }
        // }
        //
        // パラメーターを指定すれば任意のセクションを簡単に読める
        //
        AppSettingJson setting2 = new(sectionName: "customSection");
        var a2 = setting2.GetString("key1111");
        var b2 = setting2.GetInt("key2222");
        var c2 = setting2.GetFloat("key3333");
        var d2 = setting2.GetFloat("key4444"); // KeyNotFoundException
        
        
        // フルで指定すれば任意のファイルパスの任意の appSettings 以外のセクションも扱える
        AppSettingJson setting2 = new(@"d:\sample.json", "customSection");
    }
}

以上です。