セージ の メモ書き

メモこそ命の恩人だ

C# - 例外の再スロー

再スロー方法

try
{
    // ...
}
catch (Exception ex)
{
    throw;
    
    // throw ex;
    // ↑注意:この場合、スタックトレースが消える。
}
  • catch 句の中で throw を呼び出すだけ。
  • 呼び出し元のメソッドに例外が伝わる。スタックトレースも保持。

以下、試してみる。

public class SampleClass 
{
    public void Execute()
    {
        try
        {
            ExecuteCore1();
        }
        catch (Exception ex)
        {
            Debug.WriteLine("■■■ catch - その1 ■■■");
            Debug.WriteLine(ex.ToString());
            throw;
            // throw ex; ★ この場合、スタックトレースがここで消える。結果は後述参照。
        }
    }

    // ダミーのメソッドを経由し、スタックトレースを確認する。
    private void ExecuteCore1() => ExecuteCore2();
    private void ExecuteCore2() => ExecuteCore3();
    private void ExecuteCore3() => throw new NotImplementedException();
}
try
{
   new SampleClass().Execute();
}
catch (Exception ex)
{
    Debug.WriteLine("■■■ catch - その2 ■■■");
    Debug.WriteLine(ex.ToString());
}

"throw" の場合のスタックトレース

■■■ catch - その1 ■■■
System.NotImplementedException: The method or operation is not implemented.
   at SampleClass.ExecuteCore3() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 46
   at SampleClass.ExecuteCore2() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 45
   at SampleClass.ExecuteCore1() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 44
   at SampleClass.Execute() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 34

■■■ catch - その2 ■■■
System.NotImplementedException: The method or operation is not implemented.
   at SampleClass.ExecuteCore3() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 46
   at SampleClass.ExecuteCore2() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 45
   at SampleClass.ExecuteCore1() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 44
   at SampleClass.Execute() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 34
   at Program.<Main>$(String[] args) in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 17

"throw ex" の場合のスタックトレース
スタックトレースがなくなることを確認。

■■■ catch - その1 ■■■
System.NotImplementedException: The method or operation is not implemented.
   at SampleClass.ExecuteCore3() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 46
   at SampleClass.ExecuteCore2() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 45
   at SampleClass.ExecuteCore1() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 44
   at SampleClass.Execute() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 34

■■■ catch - その2 ■■■
System.NotImplementedException: The method or operation is not implemented.
   at SampleClass.Execute() in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 41
   at Program.<Main>$(String[] args) in C:\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 17



以上

C# - Exception クラス(よく使うクラス)

個人的によく使う例外クラス

  • 個人的に、よく使用するものをメモする。

引数

例外クラス 内容
ArgumentNullException 引数が null
ArgumentOutOfRangeException 引数が仕様の範囲外
FormatException 引数の書式が仕様と異なる
ArgumentException その他、引数が仕様と異なる

ファイル/フォルダー

例外クラス 内容
FileNotFoundException ファイルが無い
DirectoryNotFoundException フォルダーが無い

コレクション

例外クラス 内容
IndexOutOfRangeException 配列のインデックスが誤り
KeyNotFoundException コレクションに該当するキーが無い

その他処理

例外クラス 内容
InvalidOperationException 無効な処理
TimeoutException タイムアウト発生
NotSupportedException サポート対象外
NotImplementedException 未実装、開発中

上記の例外クラスの継承関係

https://docs.microsoft.com/ja-jp/dotnet/api/system.exception

  • Exception
    • SystemException
      • ArgumentException
        • ArgumentNullException
        • ArgumentOutOfRangeException
      • FormatException
      • IndexOutOfRangeException
      • InvalidOperationException
      • IOException
        • DirectoryNotFoundException
        • FileNotFoundException
      • KeyNotFoundException
      • NotImplementedException
      • NotSupportedException
      • TimeoutException

catch 句と継承関係

catch 句は継承関係の順に定義する

  • catch 句を複数定義する場合、派生クラスから記述すること。
  • 順序が逆の場合、ビルドエラーになる。
  • Exception クラスは最後に定義すること。
// 以下、OK
try
{
    // ...
}
catch (ArgumentNullException ex)
{
    // ...
}
catch (ArgumentException ex)
{
    // ...
}
// 以下、NG。
// 継承関係の派生側が下にあるため、ビルドエラーになる。
try
{
    // ...
}
catch (ArgumentException ex)
{
    // ...
}
catch (ArgumentNullException ex)
{
    // ...
}

catch 句は基底クラスを検知できる

try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentException ex)
{
    Debug.WriteLine(ex.GetType());
    // System.ArgumentOutOfRangeException
    
    // 基底クラスの catch 句で処理できたことを確認。
}



以上

C# - Moq

Moq

  • テストダブル用のライブラリ。.NET用。
    • テストダブル (Test Double)、Double : 代役、影武者
    • テスト対象が依存するモジュールの代役となるオブジェクト。
    • スタブ、モック etc. の総称。
  • テストダブルの作成が手間なオブジェクトを用意できる。
    • DBアクセス、通信 etc.
  • Nuget より "Moq" をインストール。
    • 作成者:Daniel Cazzulino, kzu
    • ダウンロード数:305M (2022/7時点)
    • ライセンス:BSD 3-Clause License
    • 以下のライブラリもインストールされる。
      • Castle.Core (キャッスル・コア)
  • Moq の由来は、"Mock + LINQ" だと推測。

Mock クラス

  • Mock : 模造品
  • Mock クラスにより、指定した型のモックオブジェクトを生成できる。
  • Object プロパティでモックオブジェクトにアクセスする。
  • モックオブジェクトへのアクセス時の振る舞いを定義できる。
    • Setup・Returnsm メソッド etc.
var mock = new Mock<ISample1>();
var mockObject = mock.Object;

Debug.WriteLine($"{mock.GetType()},  {mockObject.GetType()}");
// Moq.Mock`1[ISample1],  Castle.Proxies.ISample1Proxy

メソッドのモック

  • Setup メソッド
    • モックするメソッドを指定する。
    • モックしたい引数の条件を定義できる。
      • It.IsAny()
      • It.Is(x => {...})
  • Returns メソッド
    • Setup で指定したメソッドの戻り値を定義する。
    • 戻り値のロジックも定義できる。
      • Returns(x => {...})
  • 以下のメソッドの場合、モックできる。
    • interface 内のメソッド
    • abstract メソッド
    • virtual メソッド
  • それ以外のオーバーライドできないメソッドは、モックできない。
    • Setup メソッドで例外が発生する。
    • System.NotSupportedException : Non - overridable members(here: ***) may not be used in setup / verification expressions.'

以下で試してみる。

public interface ISample1
{
    public int GetIntValue(int value);
    public bool GetBoolValue(bool value);
    public string GetStringValue(string value);
}
public class Sample1 : ISample1
{
    // 入力値をそのまま返すメソッド
    public int GetIntValue(int value) => value;
    public bool GetBoolValue(bool value) => value;
    public string GetStringValue(string value) => value;
}

GetIntValue メソッドをモックする。

// 1 を入力した場合、100 を返す。
var mock = new Mock<ISample1>();
mock.Setup(x => x.GetIntValue(1)).Returns(100);

Debug.WriteLine(mock.Object.GetIntValue(1));
// 100
Debug.WriteLine(mock.Object.GetIntValue(2));
// 0
    
// 指定した通りに動作することを確認。
// 入力が 1 以外の場合、戻り値型の初期値が返る。
// 任意の数値を入力した場合、100 を返す。
var mock = new Mock<ISample1>();
mock.Setup(x => x.GetIntValue(It.IsAny<int>())).Returns(100);

Debug.WriteLine(mock.Object.GetIntValue(1));
// 100
Debug.WriteLine(mock.Object.GetIntValue(2));
// 100

// 任意の数値で、100 が返ることを確認。
// 偶数を入力した場合、100 を返す。
var mock = new Mock<ISample1>();
mock.Setup(x => x.GetIntValue(It.Is<int>(value => value % 2 == 0))).Returns(100);

Debug.WriteLine(mock.Object.GetIntValue(1));
// 0
Debug.WriteLine(mock.Object.GetIntValue(2));
// 100
Debug.WriteLine(mock.Object.GetIntValue(3));
// 0
Debug.WriteLine(mock.Object.GetIntValue(4));
// 100

// 偶数の場合に、100 が返ることを確認。
// 任意の数値を入力した場合、偶数なら 100 を返し、それ以外は入力値を返す。
var mock = new Mock<ISample1>();
mock.Setup(x => x.GetIntValue(It.IsAny<int>()))
    .Returns<int>(value => (value % 2 == 0) ? 100 : value);

Debug.WriteLine(mock.Object.GetIntValue(1));
// 1
Debug.WriteLine(mock.Object.GetIntValue(2));
// 100
Debug.WriteLine(mock.Object.GetIntValue(3));
// 3
Debug.WriteLine(mock.Object.GetIntValue(4));
// 100

// 想定通りの値が返ることを確認。


Setup・Returns メソッドを複数定義してみる。

var mock = new Mock<ISample1>();
mock.Setup(x => x.GetIntValue(1)).Returns(100);
mock.Setup(x => x.GetBoolValue(false)).Returns(true);
mock.Setup(x => x.GetStringValue("sample")).Returns("(^^)/");

Debug.WriteLine(mock.Object.GetIntValue(1));
// 100
Debug.WriteLine(mock.Object.GetBoolValue(false));
// True
Debug.WriteLine(mock.Object.GetStringValue("sample"));
// (^^) /
// モックした通りに動作することを確認

Debug.WriteLine(mock.Object.GetIntValue(2));
// 0
Debug.WriteLine(mock.Object.GetBoolValue(true));
// False
Debug.WriteLine(mock.Object.GetStringValue("abc"));
// (null)
// 入力値が未定義の場合、戻り値型の初期値が返ることを確認。
// インターフェースだけで中身がないので、当然の動作である。

プロパティのモック

  • SetupGet メソッド
    • モックするプロパティを指定する。
    • Returns メソッドで戻り値を定義する。
    • モックしたプロパティが読取専用となる。
      • 値代入時、例外は発生しない。反映されないだけ。
  • SetupProperty メソッド
    • モックするプロパティを指定する。
    • 読取専用プロパティには使用できない。
    • モックしたプロパティの読み書きが許可される。
  • 以下のプロパティの場合、モックできる。メソッドと同様。
    • interface 内のプロパティ
    • abstract プロパティ
    • virtual プロパティ

以下で試してみる。

public interface ISample1
{
    public int IntValue { get; set; }
    public int ReadonlyIntValue { get; }
}
public class Sample1 : ISample1
{
    public int IntValue { get; set; } = 1;
    public int ReadonlyIntValue { get; } = 2;
}

SetupGet メソッドで、プロパティをモックする。

var mock = new Mock<ISample1>();
mock.SetupGet(x => x.IntValue).Returns(100);
mock.SetupGet(x => x.ReadonlyIntValue).Returns(200);

Debug.WriteLine(mock.Object.IntValue);
// 100
Debug.WriteLine(mock.Object.ReadonlyIntValue);
// 200
// モックした通りの値を返すことを確認

mock.Object.IntValue = 999;
Debug.WriteLine(mock.Object.IntValue);
// 100
// モックしたプロパティが読取専用のため、値が変化しないことを確認。

// mock.Object.ReadonlyIntValue = 1000;
// 読取専用のためビルドエラーになることを確認。

SetupProperty メソッドで、プロパティをモックする。

var mock = new Mock<ISample1>();
mock.SetupProperty(x => x.IntValue, 100);
//mock.SetupProperty(x => x.ReadonlyIntValue, 200);
// ReadonlyIntValue は読取専用のため、SetupProperty メソッドの場合は例外が発生する。

Debug.WriteLine(mock.Object.IntValue);
// 100
// モックした通りの値を返すことを確認

mock.Object.IntValue = 999;
Debug.WriteLine(mock.Object.IntValue);
// 999
// モックしたプロパティの値を変更できることを確認


xUnit の使用例

DB アクセス

public record Person(int Id, string Name);

public interface IDbRepository
{
    Person Load(int id);
    bool Save(Person person);
}

public class SampleClass1 
{
    private readonly IDbRepository dbRepository;
    public SampleClass1(IDbRepository dbRepository) => this.dbRepository = dbRepository;

    public bool Run(int personId) 
    {
        // IDに対応するデータの読込
         var loadedPerson = dbRepository.Load(personId);

        // 氏名を変更して保存
        var person = loadedPerson with { Name = $"{loadedPerson}さん" };
        return dbRepository.Save(person);
    }
}
  • SampleClass1 の Run メソッドをテストしてみる。
  • DB アクセスをモックにする。
[Fact]
public void DbRepositoryTest()
{
    var person = new Person(Id: 1, "テスト");
    var mock = new Mock<IDbRepository>();
    mock.Setup(x => x.Load(It.IsAny<int>())).Returns(person);
    mock.Setup(x => x.Save(It.IsAny<Person>())).Returns(true);

    var sample = new SampleClass1(mock.Object);
    var isSucceeded = sample.Run(personId: 999);

    Assert.True(isSucceeded);
    // テストが成功したことを確認。
    // Run メソッドが通り、true が返ることを確認。
}



以上

C# - xUnit

xUnit

docs.microsoft.com

セットアップ

プロジェクトの追加

  • "追加 - 新しいプロジェクト" を選択。
  • "xUnit テスト プロジェクト" を選択。
  • "プロジェクト名.Tests" をプロジェクト名にする。

  • テストプロジェクトを作成完了。

  • 以下4つのパッケージがインストール済みの状態。

    • coverlet.collector
    • Microsoft.NET.Test.Sdk
    • xunit
    • xunit.runner.visualstudio

  • テストプロジェクトへの参照を追加。

テストクラスの追加

  • 名前空間は、テスト対象のプロジェクトに極力合わせる。
    • 管理しやすいので。
  • クラス名は、"テスト対象のクラス名 + Test" にする。
    • Sample クラスの場合、"SampleTest" クラス。


テストメソッド

以下の属性を付与すれば、テストメソッドとなる。

属性 テストメソッドの引数
Fact なし
Theory あり

Fact 属性

[Fact]
public void AddTest()
{
    var result = new SampleClass1().Add(1, 2);
    Assert.Equal(3, result);
}
  • Assert の結果でテストメソッドの成否が変わる。
  • エラーの場合、エラー箇所の情報が表示される。

Theory 属性

  • Theory の場合、テストメソッドに引数がないとエラーになる。
  • 入出力の一覧をまとめて適用できる。

InlineData 属性

[Theory]
[InlineData(1, 2, 3)]
[InlineData(1, 2, 999)]
public void AddTest(int in1, int in2, int expected)
{
    var result = new SampleClass1().Add(in1, in2);
    Assert.Equal(expected, result);
}
  • テストメソッドの上にパラメータが表示される。
    • 個人的には可読性が良い。

MemberData 属性

[Theory]
[MemberData(nameof(GetTestData))]
public void AddTest(int in1, int in2, int expected)
{
    var result = new SampleClass1().Add(in1, in2);
    Assert.Equal(expected, result);
}

private static IEnumerable<object[]> GetTestData()
{
    yield return new object[] { 1, 2, 3  };
    yield return new object[] { 1, 2, 999 };
}
  • static メソッドで定義する。
  • InlineData よりもロジックを定義できる。
  • .NET 6 で確認したところ、複数のテストケースとして表示される。
    • 以前は、単一のテストケースになっていたらしい。
    • テストケースを選択して実行しないと、"未実行" 扱いになる。

ClassData 属性

[Theory]
[ClassData(typeof(TestData))]
public void AddTest(int in1, int in2, int expected)
{
    var result = new SampleClass1().Add(in1, in2);
    Assert.Equal(expected, result);
}

private class TestData : IEnumerable<object[]>
{
    public IEnumerator<object[]> GetEnumerator()
    {
        yield return new object[] { 1, 2, 3 };
        yield return new object[] { 1, 2, 999 };
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
  • クラスでテストケースを定義する。
  • MemberData よりもロジックを定義できる。

テストメソッド(オプション)

DisplayName 属性

  • テストエクスプローラーでの名称を設定できる。
  • Fact・Theory の両方の属性で使用できる。
[Fact(DisplayName = "加算のサンプルテスト1")]
public void AddTest1()
{
    var result = new SampleClass1().Add(1, 2);
    Assert.Equal(3, result);
}

[Theory(DisplayName = "加算のサンプルテスト2")]
[InlineData(1, 2, 3)]
[InlineData(1, 2, 999)]
public void AddTest2(int in1, int in2, int expected)
{
    var result = new SampleClass1().Add(in1, in2);
    Assert.Equal(expected, result);
}

検証メソッド

メソッド 内容
Assert.Equal 値が一致するか
Assert.True 値が True であるか
Assert.Null 値が NULL であるか
Assert.Throw 指定の例外が発生するか
  • 上記は抜粋。
  • xunit の Assert クラスに定義されている。
// 以下の場合、検証を通過する。
[Fact]
public void Test()
{
    bool? dummy = false;
    Assert.Equal(false, dummy);

    dummy = true;
    Assert.True(dummy);

    dummy = null;
    Assert.Null(dummy);

    Assert.ThrowsAsync<ArgumentNullException>(() => 
    {
        throw new ArgumentNullException("test");
    });
}

ITestOutputHelper インターフェース

  • テストエクスプローラーに文字列を出力できる。
  • 以下のログをテストエクスプローラーに出力できる。
    • テストメソッドのログ
    • プロジェクトのログ(自作処理が少し必要)

テストメソッドのログを出力

public class SampleClass1Test
{
    private readonly ITestOutputHelper helper;

    public SampleClass1Test(ITestOutputHelper helper)
    {
        // XUnit 側で DI してくれる。
        this.helper = helper;
    }

    [Fact]
    public void AddTest()
    {
        helper.WriteLine($"テストを開始 (^^)/");

        var result = new SampleClass1().Add(1, 2);
        Assert.Equal(3, result);

        helper.WriteLine($"テストに成功した <result:{result}>");
    }
}
  • DI で ITestOutputHelper のオブジェクトを取得する。
  • WriteLine メソッドで出力する。

プロジェクトのログを出力

// https://docs.microsoft.com/ja-jp/dotnet/core/extensions/custom-logging-provider
public class XunitLogger : ILogger
{
    private readonly ITestOutputHelper helper;
    private readonly string categoryName;

    public XunitLogger(ITestOutputHelper helper, string categoryName)
    {
        this.helper = helper;
        this.categoryName = categoryName;
    }
    public IDisposable BeginScope<TState>(TState state) => default!;
    public bool IsEnabled(LogLevel logLevel) => true;   // 全レベル有効

    public void Log<TState>(
        LogLevel logLevel, 
        EventId eventId,
        TState state,
        Exception exception,
        Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel)) return;
        helper.WriteLine($"{logLevel}, {state}, {categoryName}");
        if (exception != null) helper.WriteLine(exception.ToString());
    }
}

public class XunitLoggerProvider : ILoggerProvider
{
    private readonly ITestOutputHelper helper;
    public XunitLoggerProvider(ITestOutputHelper helper) => this.helper = helper;
    public ILogger CreateLogger(string categoryName) => new XunitLogger(helper, categoryName);
    public void Dispose() { }
}
[Fact]
public void AddTest()
{
    var loggerFactory = new LoggerFactory();
    loggerFactory.AddProvider(new XunitLoggerProvider(helper));
    var logger = loggerFactory.CreateLogger<SampleClass1>();

    // SampleClass1 クラスのコンストラクタでログを記録する。
    var result = new SampleClass1(logger).Add(1, 2);
    Assert.Equal(3, result);
}
  • プロジェクトが ILogger を DI する場合に使用できる。
  • 自作の XunitLoggerProvider により、ログの処理を差し替える。
  • XunitLogger の Log メソッドにより、形式は自由に変更できる。



以上

C# - NLog.Extensions.Logging

NLog.Extensions.Logging

github.com

  • .NET 用のログ出力ライブラリ。
  • Microsoft の方が勤務時間外に開発したものらしい。
  • Nuget より "Microsoft.Extensions.Hosting" をインストール。
    • 作成者:Microsoft、Julian Verdurmen
    • ダウンロード数:53.1M (2022/6時点)
    • ライセンス:BSD-2-Clause
    • 依存関係のあるライブラリ (一部、省略)
  • NLog.config をプロジェクトに追加。
    • Nuget の NLog.Config は非推奨との記載あり。
    • プロパティで "新しい場合はコピーする" を選択。
    • 以下、config の例。
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <variable name="logPath" value="${basedir}/Log/" />
    <variable name="logWord" value="SampleLog" />
    <variable name="logName" value="${logPath}${logWord}.tsv" />
    <variable name="appName" value="Sample" />

    <targets async="true">
        <target
          name="logfile"
       xsi:type="File"
          encoding="UTF-8"
          fileName="${logName}"
          archiveFileName="${logPath}OldLogs/${logWord}_{##########}.zip"
          archiveAboveSize="10000000"
          maxArchiveFiles="100"
          archiveNumbering="Sequence"
          archiveEvery="None"
          archiveOldFileOnStartup="true"
          enableArchiveFileCompression="true">
            <layout xsi:type="CsvLayout" delimiter="Tab" withHeader="false">
                <column name="time" layout="${date:format=yyyy-MM-ddTHH\:mm\:ss.fffK}" />
                <column name="level" layout="${level:padding=-5}"/>
                <column name="processname" layout="${appName} " />
                <column name="threadid" layout="${threadid:padding=4}" />
                <column name="message" layout="${replace-newlines:replacement=&lt;br&gt;:${message:format=ToString}}"/>
                <column name="callsite" layout="${callsite}#${callsite-linenumber}" />
                <column name="stacktrace" layout="${stacktrace:topFrames=10}" />
                <column name="exception" layout="${replace-newlines:replacement=&lt;br&gt;:${exception:format=ToString}}"/>
            </layout>
        </target>
    </targets>

    <rules>
        <logger name="*" minlevel="Debug" writeTo="logfile" />
    </rules>
</nlog>

ロガーの利用方法

static オブジェクトの利用

LogManager.SetLoggerFactory(new NLogLoggerFactory());

var sample = new Class1();
// カレントにログファイルが生成されたことを確認。

汎用ホストの利用

  • DI によりロガーを取得する。
    • Nuget より "Microsoft.Extensions.Hosting" をインストール
  • AddNLog メソッドで追加できる。
    • NLog.config のファイルパスを設定する。
public class Class1
{
    public Class1(ILogger<Class1> logger)
    {
        logger.LogInformation("お試し (^^♪");
    }
}
IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureLogging((hostContext, loggingBuilder) =>
    {
        loggingBuilder.ClearProviders();
        
        // NLog.config のファイルパスを設定
        loggingBuilder.AddNLog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config"));
    })
    .ConfigureServices(services =>
    {
        services.AddTransient<Class1>();
    })
    .Build();

var sample = host.Services.GetService<Class1>();
// カレントにログファイルが生成されたことを確認。

NLog.Config の設定方法

Configuration file · NLog/NLog Wiki · GitHub

targets タグ

Targets · NLog/NLog Wiki · GitHub

xsi:type

Config | NLog

xsi:type="File" 用のパラメータ

xsi:type="File" で使用するパラメータの一例を以下に記載する。

name

  • ターゲット名。
  • rules への関連付けに使用する。

fileName

  • 出力ファイル名。

layout

  • 出力項目のレイアウト。

archiveAboveSize

  • ログサイズが指定バイトに到達すればアーカイブされる。
  • 単位はバイト。例えば10MBを上限にするなら、10000000に設定する。

maxArchiveFile

  • アーカイブファイルの上限数。
  • 例えば、maxArchiveFiles="3"にした場合、4ファイル目が生成された時点で、最も古いファイルが削除される。

archiveOldFileOnStartup

  • true の場合、起動時に旧ファイルがアーカイブされる。
  • false の場合、起動時に旧ファイルがアーカイブされない。
  • 既定 : false

archiveNumbering

  • アーカイブ方法。
  • Rolling
    • アーカイブファイル名が全て修正される。
    • 0が最新になる。
  • Date
    • アーカイブファイル名が日付で出力される。
    • 古いファイルと同名なら上書きされる。
  • Sequence
    • 新しい数値が採番される。
  • DateAndSequence
    • アーカイブファイル名が日付+シーケンスNo.で出力される。

ArchiveEvery

  • アーカイブのタイミング。
  • ArchiveEveryを使用する場合、ファイル名は静的にする必要がある。日付を含める etc. 動的なファイル名にしないこと。

enableArchiveFileCompression

  • true の場合、zipファイルを生成できる。
  • false の場合、拡張子zipのファイルは生成されるが、解凍はできないし、trueとサイズ比較すると、圧縮されてない様子。
  • 同じ処理のログファイルサイズ、true->5KB、false:800KB だった。圧縮効率も良いみたい。

rules タグ

https://github.com/nlog/nlog/wiki/Configuration-file#rules

パラメータ名 内容
name ロガーの名称( ワイルドカード * でも良い )
minlevel 最小のログレベル
writeTo ターゲット名 ( ターゲットの設定に従ってログ出力される )

補足

NLog.Config の初期状態

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">

  <!-- optional, add some variables
  https://github.com/nlog/NLog/wiki/Configuration-file#variables
  -->
  <variable name="myvar" value="myvalue"/>

  <!--
  See https://github.com/nlog/nlog/wiki/Configuration-file
  for information on customizing logging rules and outputs.
   -->
  <targets>

    <!--
    add your targets here
    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
    -->

    <!--
    Write events to a file with the date in the filename.
    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    -->
  </targets>

  <rules>
    <!-- add your logging rules here -->

    <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    <logger name="*" minlevel="Debug" writeTo="f" />
    -->
  </rules>
</nlog>



以上

C# - Microsoft.Extensions.Logging

Microsoft.Extensions.Logging

www.nuget.org

docs.microsoft.com

  • ロギング用のライブラリ。
  • ILogger インタフェースにロガーを注入して利用する。
  • 以下に出力できる。
    • デバッグウィンドウ
    • コンソール
    • OSのイベントログ
  • Microsoft 以外のライブラリも使用できる。
    • NLog、Log4Net など
    • DI により、使用ライブラリを容易に切り替えられる。
  • Nuget より "Microsoft.Extensions.Logging" をインストール。
    • 作成者:Microsoft
    • ダウンロード数:1.23B (2022/6時点)
    • ライセンス:MIT

WPFアプリでデバッグウィンドウに出力してみる。

using ILoggerFactory loggerFactory = LoggerFactory.Create(loggingBuilder =>
{
    // ログレベルの最小値を設定
    loggingBuilder.SetMinimumLevel(LogLevel.Trace);
    // デバッグウィンドウに記録
    // Nuget より "Microsoft.Extensions.Logging.Debug" をインストールする。
    loggingBuilder.AddDebug();
});
ILogger logger = loggerFactory.CreateLogger<MainWindow>();

logger.LogTrace("お試し1");
logger.LogDebug("お試し2");
logger.LogInformation("お試し3");
logger.LogWarning("お試し4");
logger.LogError("お試し5");
logger.LogCritical("お試し6");

// デバッグウィンドウに以下が記録されたことを確認。
// WpfApp1.MainWindow: Trace: お試し1
// WpfApp1.MainWindow: Debug: お試し2
// WpfApp1.MainWindow: Information: お試し3
// WpfApp1.MainWindow: Warning: お試し4
// WpfApp1.MainWindow: Error: お試し5
// WpfApp1.MainWindow: Critical: お試し6

コンソールアプリでも試す。Nuget より以下をインストールしておく。
Microsoft.Extensions.Logging.Debug
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.EventLog

using ILoggerFactory loggerFactory = LoggerFactory.Create(loggingBuilder =>
{
    // 複数指定することもできる。
    loggingBuilder.AddDebug();
    loggingBuilder.AddConsole();
    loggingBuilder.AddEventLog();
});
ILogger logger = loggerFactory.CreateLogger<Program>();

logger.LogInformation("お試し (^^♪");
// ログが以下に記録されたことを確認。
// ・デバッグウィンドウ
// ・コンソールアプリ
// ・OSイベントログ

ロガーの利用方法

  • 前述の方法で都度生成するのは手間である。
  • 汎用ホストの利用がオススメ。

static オブジェクトの利用

public static class LogManager
{
    private static ILoggerFactory loggerFactory = new LoggerFactory();
    private static readonly object lockObject = new();

    public static void SetLoggerFactory(ILoggerFactory factory) => loggerFactory = factory;

    static public ILogger<T> GetLogger<T>()
    {
        lock (lockObject)
        {
            return loggerFactory.CreateLogger<T>();
        }
    }
}

public class Class1
{
    public Class1()
    {
        // ロガーを取得
        var logger = LogManager.GetLogger<Class1>();
        logger.LogInformation("お試し (^^♪");
    }
}
// ILoggerFactory を設定
LogManager.SetLoggerFactory(LoggerFactory.Create(loggingBuilder =>
{
    loggingBuilder.AddDebug();
}));

var sample = new Class1();
// お試し (^^♪
// ログが記録されたことを確認。

汎用ホストの利用

  • DI によりロガーを取得する。
  • Nuget より "Microsoft.Extensions.Hosting" をインストール。
    • 作成者:Microsoft
    • ダウンロード数:197M (2022/6時点)
    • ライセンス:MIT
public class Class1
{
    public Class1(ILogger<Class1> logger)
    {
        logger.LogInformation("お試し (^^♪");
    }
}
IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureLogging(loggingBuilder => 
    {
        loggingBuilder.ClearProviders();
        loggingBuilder.AddDebug();
    })
    .ConfigureServices(services =>
    {
        services.AddTransient<Class1>();
    })
    .Build();

var sample = host.Services.GetService<Class1>();
// お試し (^^♪
// ログが記録されたことを確認。

汎用ホストの利用(WPF

  • ViewModel の DI は難しい。
    • ViewModel のコンストラクタに引数があると、XAML のデザイナーでエラーになる。
    • View のコードビハインドで DataContext にセットする場合、new する必要がある。
    • なので、DI させる方法がわからない。。。
  • ViewModel ではユニットテストしないと割り切って、DI コンテナを参照する。
    • アンチパターンかも。。。
    • DIコンテナにロガー、DBアクセスなどのオブジェクトをセットする。
    • それを ViewModel で Model 用のオブジェクトに適用していくのが良いかも。
// エントリーポイント
public partial class App : Application
{
    private static readonly IHost host = Host.CreateDefaultBuilder()
            .ConfigureLogging(loggingBuilder =>
            {
                loggingBuilder.ClearProviders();
                loggingBuilder.AddDebug();
            })
            .Build();

    public static IServiceProvider ServiceProvider => host.Services;

    public static ILogger<T> GetLogger<T>() => host.Services.GetRequiredService<ILogger<T>>();

    private async void Application_Startup(object sender, StartupEventArgs e)
    {
        StartupUri = new Uri(@"/MainWindow.xaml", UriKind.Relative);

        await host.RunAsync();
    }
}
// Model
public class MainService
{
    private readonly ILogger<MainService> logger;

    public MainService(ILogger<MainService> logger) => this.logger = logger;

    public void Run() => logger.LogInformation("お試し (^^♪");
}
// ViewModel
public class MainViewModel
{
    private readonly ILogger<MainViewModel> logger;
    private readonly MainService mainService;

    public MainViewModel()
    {
        // DI コンテナを間接的に参照し、ロガーを取得
        logger = App.GetLogger<MainViewModel>();
        logger.LogInformation("お試し (^^♪");

        // Model には ViewModel からセット
        mainService = new MainService(App.GetLogger<MainService>());
        mainService.Run();
    }
}



以上

Nuget - ダウンロード数の単位 (K、M、B、T)

数の単位

略称 英語
K Kilo 1,000
M Million 1,000,000 (100万)
B Billion 1,000,000,000 (10億)
T Trillion 1,000,000,000,000 (1兆)

K (キロ)、M(メガ)と思ってたけど、M は Million だった。で、B が Billion。
"Microsoft.Extensions.Logging" が 1.22B件 (12.2億件) もダウンロードされてた。



以上

Azure - Cosmos DB

Azure Cosmos DB

docs.microsoft.com

azure.microsoft.com

Free Tier

docs.microsoft.com

  • 最初の 1000 RU/秒と 25 GB のストレージを無料で利用できる。
  • Free レベルは、サブスクリプションにつき 1 つまで。
  • "サーバーレス" の場合、選択できない。(2022/6時点)

Azure Cosmos DB アカウントの作成

  • "Azure Cosmos DB" を選択し、"作成" を選択。
  • "コア (SQL) - 推奨" の "作成" を選択。

  • "プロビジョニングされたスループット" を選択。

  • "Free レベルの割引" で "適用" を選択。

  • 各パラメータを設定。

  • 数分でデプロイが完了。

コンテナの作成

  • ".NET" を選択し、手順1~3 を行う。

  • "New Container" を選択。

  • コンテナー情報を入力し、"OK" を選択。

  • コンテナーの作成が完了。

データの利用



以上

C# - MTBF/MTTF/稼働率

MTBF/MTTF

名称 意味 算出方法
MTBF (Mean Time Between Failure) 平均故障間隔 稼働時間の合計 / 稼働回数
MTTR (Mean Time To Repair) 平均復旧時間 復旧時間の合計 / 故障回数
  • 信頼性を示す指標の一つ。
  • 算出方法は、該当時間の平均値を求めるだけ。

以下の状況を考える。

区間 稼働時間 復旧時間
1 400時間 24時間
2 600時間 36時間
3 500時間 48時間
public record OperationTime(float OperatedTime, float RepairedTime);
var timeList = new List<OperationTime>() 
{
    new OperationTime(400, 24),
    new OperationTime(600, 36),
    new OperationTime(500, 48),
};

var mtbf = timeList.Average(x => x.OperatedTime);
Debug.WriteLine($"MTBF : {mtbf} 時間");
// MTBF : 500 時間

var mttr = timeList.Average(x => x.RepairedTime);
Debug.WriteLine($"MTTR : {mttr} 時間");
// MTTR : 36 時間

稼働率

  • 可用性を示す指標の一つ。
  • 算出方法は二通り。
    • 1.稼働時間 / 総運用時間
    • 2.MTBF / (MTBF + MTTR)
  • 2を分解して考えると、1になる。結局同じこと。

前述の運用時間で計算してみる。

var operatedTimeSum = timeList.Sum(x => x.OperatedTime);
var repairedTimeSum = timeList.Sum(x => x.RepairedTime);
var operationRate1 = operatedTimeSum / (operatedTimeSum + repairedTimeSum);
Debug.WriteLine($"稼働率 : {operationRate1}");
// 稼働率 : 0.9328358

var operationRate2 = mtbf / (mtbf + mttr);
Debug.WriteLine($"稼働率 : {operationRate2}");
// 稼働率 : 0.9328358

稼働率と復旧時間

稼働率と感じても、基準を変えると、意外と復旧時間が長いと感じる。
基準×稼働率ごとの復旧時間は以下の通り。

基準 99.0% 99.5% 99.9% 99.95% 99.995%

(365日)
3日と
15時間36分0秒
1日と
19時間48分0秒
8時間45分36秒 4時間22分48秒 26分17秒

(31日)
7時間26分24秒 3時間43分12秒 44分38秒 22分19秒 2分14秒

(7日)
1時間40分48秒 50分24秒 10分5秒 5分2秒 30秒
TimeSpan GetRepairedTime(int days, double operationRate) 
{
    var repairedRate = 1 - (operationRate / 100);
    return new TimeSpan(days, hours: 0, minutes: 0, seconds: 0) * repairedRate;
}



以上

Azure - 可用性セット/可用性ゾーン/リージョン/ジオ

可用性セット

  • 同一データセンター内のハードウェアの冗長化。(ラック、ブレード)
  • ハードウェアの障害発生時にも、サービスを継続できる。

可用性ゾーン

docs.microsoft.com

  • "ゾーン = データセンター" である。
  • "可用性ゾーン" とは、複数のデータセンターにより冗長化されたサービス。
  • つまり、二つ以上のデータセンターで構成されるサービス。
  • データセンター規模の障害発生時にも、サービスを継続できる。
  • データセンターの具体的な場合は非公開。

リージョン

azure.microsoft.com

  • 一つ以上の "可用性ゾーン" で構成される地域。
  • 日本の場合、以下の二つ。(2022/6 時点)

    リージョン 可用性ゾーンの数 場所
    東日本 1~3 埼玉、東京
    西日本 1 大阪

VM 作成時の可用性ゾーンの設定画面。
東日本の場合は3つまで、西日本の場合は1固定。

リージョンペア

  • 複数リージョン構成する際に最適な組合せ。
  • 日本の場合、東日本と西日本のリージョン。
  • リージョン間の距離:約500km以上
  • リージョン規模の障害発生時にも、サービスを継続できる。
  • リージョンペアのリージョンは、極力同じ時間帯にメンテナンスされない。

ジオ

  • 一つ以上の "リージョン" で構成される地域。
  • 国や、複数の国にまたがるジオもある。
  • 日本の場合、日本。



以上

Azure - コスト管理

コストの確認

  • "全般 - コストの管理と請求" を選択。

  • "概要" からコストの概要を確認できる。

  • "コスト分析" から詳細を確認できる。

アラートの設定

  • "コストのアラート" より "作成" を選択。
  • アラートの閾値の金額を設定。



以上

Azure - Azure CLI

Azure CLI

docs.microsoft.com

セットアップ

az コマンド

コマンド 内容
az login Azure にログインする
az logout Azure からログアウトする
az account show アカウント情報を表示する

ToDo: 色々試す。



以上

AZURE - 関数アプリ (Azure Functions)

関数アプリ (Azure Functions)

docs.microsoft.com

  • サーバーレスなサービス。
  • 動作環境については、心配無用。
  • コードを記述に専念できる。

作成手順

  • リソースグループを作成する。

  • "関数アプリ" を選択し、"作成" を選択。

  • 必要な情報を入力。

    • プランは "消費量(サーバーレス)" にする。従量課金。

  • ストレージアカウントを未作成の場合、新規作成する。

    • そこまで使わなければ、無料だと思う... 今のところ無料。

  • 関数アプリを作成できた。

  • 完了後、ストレージアカウントも作成されたことを確認。


開発環境

Azure ポータル の場合

  • 前述で作成した関数アプリを選択し、"作成" を選択。
  • 開発環境を "ポータルでの開発" にし、"HTTP trigger" を選択。
  • "テストと実行" を選択。

以下がデフォルトのコード。

#r "Newtonsoft.Json"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    string name = req.Query["name"];

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic data = JsonConvert.DeserializeObject(requestBody);
    name = name ?? data?.name;

    string responseMessage = string.IsNullOrEmpty(name)
        ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                : $"Hello, {name}. This HTTP triggered function executed successfully.";

            return new OkObjectResult(responseMessage);
}
  • HTTP リクエストの内容を設定し、"実行" を選択。
  • HTTP レスポンスを確認できる。

Visual Studio の場合

  • 新しいプロジェクトを作成し、"Azure Functions" を選択。
  • "HTTP trigger" を選択。

  • ローカル環境でデバッグできる。

  • プロジェクト→発行 より、"Azure Funciton App (Windows)" を選択。

  • Microsoft アカウントと紐づいた Azure のサブスクリプションが表示される。

  • 関数アプリを選択し、"完了" を選択。
  • "発行" を選択し、2~3分ほど待つと、処理が完了する。

  • Azure 上にデプロイされる。

  • "テストと実行" より、前述と同様の操作方法で動作確認できる。


関数アプリの利用

https://関数アプリ名.azurewebsites.net/api/関数名?code=関数キー
  • C# を使用して関数アプリを利用する。
  • "関数の URL の取得" よりURLを取得する。
  • 関数にアクセスするため、関数キーが必要。

GET メソッドの場合

  • HttpClient.GetAsync メソッドで取得できる。
  • クエリストリングにパラメータを指定する。
private async Task RunGetMethod(string url) 
{
    using var client = new HttpClient();
    var responseMessage = await client.GetAsync($"{url}&name=テストさん");
    if (!responseMessage.IsSuccessStatusCode) return;
    var json = await responseMessage.Content.ReadAsStringAsync();
    Debug.WriteLine(json);
    // Hello, テストさん. This HTTP triggered function executed successfully.
}

POST メソッド

  • HttpClient.PostAsync メソッドで取得できる。
  • ボディ部に JSON 形式でパラメータを指定する。
private async Task RunPostMethod(string url)
{
    using var client = new HttpClient();
    var content = "{ name: 'テストさん' }";
    var responseMessage = await client.PostAsync(url, new StringContent(content));
    if (!responseMessage.IsSuccessStatusCode) return;
    var json = await responseMessage.Content.ReadAsStringAsync();
    Debug.WriteLine(json);
    // Hello, テストさん. This HTTP triggered function executed successfully.
}



以上

C# - OpenWeatherMap

OpenWeatherMap

openweathermap.org

  • 気象データを取得できるサービス。
  • WEB API により取得。API キーの取得が必要。
  • 都市名や郵便番号などを指定して取得できる。

API キーの取得

  • OpenWeatherMap でアカウントを作成する。

  • メールで API キーを取得できる。サインイン後の画面からも確認できる。


API の利用方法

openweathermap.org

  • 上記リンクを参照。
  • 各サービスの仕様が公開されている。
  • リクエスト/レスポンスの仕様がわかる。

以下、現在の気象データを取得する方法。

openweathermap.org

パラメータ 内容
q "都市名,国コード Tokyo,jp
units 測定単位 metric (メートル法)
lang 言語 ja
appid APIキー 32桁の英数字

ブラウザ or curl で確認。

http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp&units=metric&lang=ja&appid=APIキー

以下のデータを取得できたことを確認。

{"coord":{"lon":139.6917,"lat":35.6895},"weather":[{"id":801,"main":"Clouds","description":"薄い雲","icon":"02d"}],"base":"stations","main":{"temp":35.04,"feels_like":40.38,"temp_min":29.75,"temp_max":37.66,"pressure":1005,"humidity":49},"visibility":10000,"wind":{"speed":9.77,"deg":190},"clouds":{"all":20},"dt":1656135671,"sys":{"type":2,"id":2038398,"country":"JP","sunrise":1656098792,"sunset":1656151250},"timezone":32400,"id":1850144,"name":"東京都","cod":200}


C# からの利用

  • 取得データをデシリアライズする。
  • クラスは、JSON 文字列から自動生成する。 (Visual Stuido の機能)
public class Rootobject
{
    public Coord coord { get; set; }
    public Weather[] weather { get; set; }
    public string _base { get; set; }
    public Main main { get; set; }
    public int visibility { get; set; }
    public Wind wind { get; set; }
    public Rain rain { get; set; }
    public Clouds clouds { get; set; }
    public int dt { get; set; }
    public Sys sys { get; set; }
    public int timezone { get; set; }
    public int id { get; set; }
    public string name { get; set; }
    public int cod { get; set; }
}

public class Coord
{
    public float lon { get; set; }
    public float lat { get; set; }
}

public class Main
{
    public float temp { get; set; }
    public float feels_like { get; set; }
    public float temp_min { get; set; }
    public float temp_max { get; set; }
    public int pressure { get; set; }
    public int humidity { get; set; }
    public int sea_level { get; set; }
    public int grnd_level { get; set; }
}

public class Wind
{
    public float speed { get; set; }
    public int deg { get; set; }
    public float gust { get; set; }
}

public class Rain
{
    public float _1h { get; set; }
}

public class Clouds
{
    public int all { get; set; }
}

public class Sys
{
    public int type { get; set; }
    public int id { get; set; }
    public string country { get; set; }
    public int sunrise { get; set; }
    public int sunset { get; set; }
}

public class Weather
{
    public int id { get; set; }
    public string main { get; set; }
    public string description { get; set; }
    public string icon { get; set; }
}
using var client = new HttpClient();

var apikey = "********************************";
var responseMessage = await client.GetAsync($"http://api.openweathermap.org/data/2.5/weather?q=Tokyo,jp&units=metric&lang=ja&appid={apikey}");

if (!responseMessage.IsSuccessStatusCode) return;

var json = await responseMessage.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Rootobject>(json);

var coord = result.coord;
var main = result.main;
var weather = result.weather.FirstOrDefault();
var summary =
    $"都市名:{result.name}, 経度:{coord.lon}, 緯度:{coord.lat}, " +
    $"温度:{main.temp}, 最高気温:{main.temp_max}, 最低気温:{main.temp_min}, " +
    $"湿度:{main.humidity}%, 大気圧:{main.pressure}, " +
    $"気象条件:{weather.description}";
Debug.WriteLine(summary);
// 都市名:東京都, 経度:139.6917, 緯度:35.6895, 温度:35.63, 最高気温:37.11, 最低気温:31.55, 湿度:46%, 大気圧:1004, 気象条件:薄い雲

// 気象データを取得できたことを確認。



以上

Visual Studio - JSON → クラスの自動生成

JSON → クラスの自動生成

  • 手順

    • JSON の文字列データをクリップボードにコピー。
    • C# のファイル (***.cs) を表示。
    • 編集→"JSON をクラスとして貼り付ける"

  • 環境

  • 使用例
    • WEB API で取得した JSON からクラスを生成。
  • 注意
    • JSON 文字列では、元のデータ型を判定できない。
    • そのため、自動生成したクラスと元のクラスが完全に一致するとは限らない。
    • クラスのたたき台として利用すること。

以下のクラスで試してみる。
リアライザーは JSON.NET を使用する。

public class SampleClass1
{
    public bool BoolValue { get; set; }

    public byte ByteValue { get; set; }

    public int IntValue { get; set; }
    public uint UintValue { get; set; }

    public float FloatValue { get; set; }
    public double DoubleValue { get; set; }

    public char CharValue { get; set; }
    public string StringValue { get; set; }

    public SampleClass2 Sample2 { get; set; }

    public IEnumerable<SampleClass2> Sample2List { get; set; }
    public IDictionary<int, SampleClass2> Sample2Dic { get; set; }
}

public class SampleClass2
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

まず、お試し用の JSON を生成する。

var sample = new SampleClass1()
{
    BoolValue = true,
    ByteValue = 10,
    IntValue = 11,
    UintValue = 12,
    FloatValue = (float)3.14,
    DoubleValue = 3.15,
    CharValue = 'a',
    StringValue = "Test",
    Sample2 = new SampleClass2()
    {
        Value1 = 1,
        Value2 = 2,
    },
    Sample2List = new List<SampleClass2>()
    {
        new SampleClass2() { Value1 = 10, Value2 = 20 },
        new SampleClass2() { Value1 = 30, Value2 = 40 },
    },
    Sample2Dic = new Dictionary<int, SampleClass2>()
    {
        [1] = new SampleClass2() { Value1 = 10, Value2 = 20 },
        [2] = new SampleClass2() { Value1 = 30, Value2 = 40 },
    },
};

var serializedInfo = JsonConvert.SerializeObject(sample);
Debug.WriteLine(serializedInfo);

{"BoolValue":true,"ByteValue":10,"IntValue":11,"UintValue":12,"FloatValue":3.14,"DoubleValue":3.15,"CharValue":"a","StringValue":"Test","Sample2":{"Value1":1,"Value2":2},"Sample2List":[{"Value1":10,"Value2":20},{"Value1":30,"Value2":40}],"Sample2Dic":{"1":{"Value1":10,"Value2":20},"2":{"Value1":30,"Value2":40}}}

生成した JSON 文字列をクリップボードにコピーし、クラスとして貼り付ける。

public class Rootobject
{
    public bool BoolValue { get; set; }
    public int ByteValue { get; set; }
    public int IntValue { get; set; }
    public int UintValue { get; set; }
    public float FloatValue { get; set; }
    public float DoubleValue { get; set; }
    public string CharValue { get; set; }
    public string StringValue { get; set; }
    public Sample2 Sample2 { get; set; }
    public Sample2list[] Sample2List { get; set; }
    public Sample2dic Sample2Dic { get; set; }
}

public class Sample2
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

public class Sample2dic
{
    public _1 _1 { get; set; }
    public _2 _2 { get; set; }
}

public class _1
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

public class _2
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

public class Sample2list
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

クラスを自動生成できた。デシリアライズできるレベルになってる。
ただ、この機能で元のデータ型への復元は難しい。テンプレートとして扱うのが良さそう。

元のデータ型との差異

元のデータ型 自動生成後のデータ型
SampleClass1 Rootobject
byte、uint int
double float
char string
IEnumerable Sample2list[]
IDictionary<int, SampleClass2> Sample2dic


自動生成後のデータ型でデシリアライズできるか試す。

// ...
var serializedInfo = JsonConvert.SerializeObject(sample);
Debug.WriteLine(serializedInfo);

// 前述のコードに以下を追記。

var deserializedInfo = JsonConvert.DeserializeObject<Rootobject>(serializedInfo);

シリアライズできることを確認。
Dictionary 以外は元のデータが格納されたことを確認。



以上