C#でテストデータをCSVから取り込む

# 1. はじめに
* C#で単体テストで使用するデータをCSVから取り込みたい
* DTOとCSVを属性でマッピングして毎回マッピングの設定をしないようにしたい
* CSVファイルはDBの文字コードに合わせてSHIFT-JISを指定したい
* DTOはToStringを自動でオーバーライドできるようにしたい

# 2. 開発環境
* Visual Studio 2022
* Windows 11
* Csv Helper (Nuget)
* System.Text.Encoding.CodePages (Nuget)
* ToString.Fody (Nuget)

# 3. 開発環境準備
* NuGetよりインストールする
## 3.1. CsvHelperのインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/4f489ff7-5b9b-d5c2-530f-f706712f7df5.png)

## 3.2. System.Text.Encoding.CodePagesのインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/8be3d90b-09d2-7971-04c1-f40ada171cdf.png)

## 3.3. ToString.Fodyのインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/a7a9f754-900f-12c1-c388-a3e4a0629b50.png)

# 4. サンプルプログラム
## 4.1. DTOクラス
* Name属性を追加してCSVとDTOのマッピングを指定する
```csharp:FooDto.cs
using CsvHelper.Configuration.Attributes;

namespace WinFormsApp1
{
    [ToString]
    public class FooDto
    {
        [Name("No")]
        public int No { get; set; }            //No

        [Name("Name")]
        public string? Name { get; set; }       //名前
    }
}
```
## 4.2. CSVクラス
* [追加] > [テキストファイル]からCSVファイルを追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/cf6bf656-2863-54a5-b9cc-c916515303fc.png)
* 1件のDTOを取得する場合のCSVファイル
```FooDto2.csv
No,Name
#ここからフィールドです。
1,名前1
```
* 複数のDTOを取得する場合のCSVファイル
```FooDtos.csv
No,Name
#ここからフィールドです。
1,名前1
2 ,"名前 2"

3 ,"名前 3"
```
* CSVファイルはプロパティより出力ディレクトリにコピーを```常にコピーする```に変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/82c2ef98-e184-1095-96b3-dffff9674c55.png)

## 4.3. ユーティリティクラス
* 1件取得用と複数件取得用でメソッドを分けたが、冗長なので1つできないか検討中
```csharp:CsvLoaderUtil.cs
using CsvHelper.Configuration;
using CsvHelper;
using System.Globalization;
using System.Text;


namespace WinFormsApp1
{
    public class CsvLoaderUtil
    {
        // エンコーディング設定
        private static EncodingProvider _provider;
        private static Encoding _encoding;

        // CSVHelperのConfiguration設定
        private static CsvConfiguration _config;

        static CsvLoaderUtil()
        {
            // エンコーディング設定
            _provider = System.Text.CodePagesEncodingProvider.Instance;
            _encoding = _provider.GetEncoding("shift-jis")!;

            // CSVHelperのConfiguration設定
            _config = new CsvConfiguration(CultureInfo.InvariantCulture)
            {
                // ヘッダー有り
                HasHeaderRecord = true,
                // カンマ区切り
                Delimiter = ",",
                // 実行環境で定義されている改行コード
                NewLine = Environment.NewLine,
                // 空白を無視する
                IgnoreBlankLines = true,
                // Shift-JIS
                Encoding = _encoding,
                // コメント許可
                AllowComments = true,
                Comment = '#',
                // 列数をチェックする
                DetectColumnCountChanges = true,
                // 空白を削除
                TrimOptions = TrimOptions.Trim,
            };
        }

        /// <summary>
        /// CSVファイル読み込みDTOを1件返却する
        /// </summary>
        /// <typeparam name="T">DTO</typeparam>
        /// <param name="filename">CSVファイル名</param>
        /// <returns>T</returns>
        public static T load<T>(string filename) where T : class
        {
            // CSVファイルの読み込み
            using (var reader = new StreamReader(filename, _encoding))
            using (var csv = new CsvReader(reader, _config))
            {
                return csv.GetRecords<T>().First();
            }
        }

        /// <summary>
        /// CSVファイル読み込みList形式で返却する
        /// </summary>
        /// <typeparam name="T">DTO</typeparam>
        /// <param name="filename">CSVファイル名</param>
        /// <returns>List<T></returns>
        public static List<T> loads<T>(string filename) where T : class
        {
            // CSVファイルの読み込み
            using (var reader = new StreamReader(filename, _encoding))
            using (var csv = new CsvReader(reader, _config))
            {
                return csv.GetRecords<T>().ToList();
            }
        }

    }
}
```

# 5. 動作確認
## 5.1. 動作確認用プログラム
```csharp
private void button1_Click(object sender, EventArgs e)
{
    // DTOを1件取得する
    Debug.Print("DTOを1件取得する");
    var result = CsvLoaderUtil.load<FooDto>("FooDto.csv");
    Debug.Print(result.ToString());

    // DTOを複数件取得する
    Debug.Print("DTOを複数件取得する");
    var results = CsvLoaderUtil.loads<FooDto>("FooDtos.csv");
    foreach (var item in results)
    {
        Debug.Print(item.ToString());
    }
}
```
## 5.2. 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/73992036-e322-41fe-d5c4-86bbec386d87.png)

# 6. 参考文献

https://marunaka-blog.com/csharp-csvhelper-csv-read-write/4690/

https://qiita.com/kwsmax/items/35036408d66b89e659b8