Magicodes.IEにおけるインポート・エクスポートフィルターの利用方法

全体設計

Magicodes.IEはインポートとエクスポートをサポートする汎用ライブラリであり、Dtoのインポート/エクスポートおよび動的エクスポートに対応しており、Excel、Word、Pdf、Csv、Htmlをサポートしています。本記事では、Magicodes.IEのインポート・エクスポートフィルターの使い方について説明します。開始前に、Magicodes.IEが現在サポートしているフィルターについて確認しましょう:

インターフェース 説明
IImportResultFilter インポート結果フィルター。インポート結果や検証エラー情報を変更できます(例:エラーマーキングを動的に変更)
IImportHeaderFilter インポートヘッダー・フィルター。列名や値マッピングリストなどを変更できます
IExporterHeaderFilter エクスポートヘッダー・フィルター。ヘッダー、インデックス、値マッピングなどを変更できます

インポート結果フィルター(IImportResultFilter)の使用法

インポート結果フィルターは、インポート結果や検証エラー情報の変更が可能です(例:エラーメッセージを動的に変更)。これはインポートデータとエラーバリデーション内容を二度目の動的処理に適しており、カスタム検証ロジックや多言語メッセージ翻訳などが可能です。以下で実践的な例を紹介します:

インポートファイルの準備

以下の図のように、Excelファイルを準備します:

ダウンロードリンク

Dtoの作成

Excelファイルを準備した後、Dtoを作成します:

[ExcelImporter(ImportResultFilter = typeof(ImportResultFilterTest), IsLabelingError = true)]
public class ImportResultFilterDataDto1
{
    /// <summary>
    ///     製品名
    /// </summary>
    [ImporterHeader(Name = "製品名")]
    public string Name { get; set; }

    /// <summary>
    ///     製品コード
    ///     長さ検証
    ///     繰り返し検証
    /// </summary>
    [ImporterHeader(Name = "製品コード", Description = "最大長は20です", AutoTrim = false, IsAllowRepeat = false)]
    public string Code { get; set; }
}

上記コードでは、「ImportResultFilterDataDto1」というDtoを作成し、ExcelImporter属性のImportResultFilterプロパティでインポート結果フィルターの型を指定しています。

IImportResultFilterインターフェースを実装したクラスの作成

次に、IImportResultFilterインターフェースを実装したクラスを作成します:

public class ImportResultFilterTest : IImportResultFilter
{
    /// <summary>
    /// この例では、データの検証エラー結果を変更し、多言語などに使用できます
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="importResult"></param>
    /// <returns></returns>
    public ImportResult<T> Filter<T>(ImportResult<T> importResult) where T : class, new()
    {
        var errorRows = new List<int>()
        {
            5,6
        };
        var items = importResult.RowErrors.Where(p => errorRows.Contains(p.RowIndex)).ToList();

        for (int i = 0; i < items.Count; i++)
        {
            for (int j = 0; j < items[i].FieldErrors.Keys.Count; j++)
            {
                var key = items[i].FieldErrors.Keys.ElementAt(j);
                var value = items[i].FieldErrors[key];
                items[i].FieldErrors[key] = value?.Replace("データの重複があります。確認してください。行番号:", "Duplicate data exists, please check! Row:");
            }
        }
        return importResult;
    }
}

上記コードでは、重複エラーのメッセージを「Duplicate data exists, please check! Row:」に変更しています。次に、インポートコードを記述します:

インポートコードの記述

 public async Task ImportResultFilter_Test()
    {
        var filePath = Path.Combine(Directory.GetCurrentDirectory(), "TestFiles", "Errors", "データエラー.xlsx");
        var labelingFilePath = Path.Combine(Directory.GetCurrentDirectory(), $"{nameof(ImportResultFilter_Test)}.xlsx");
        var result = await Importer.Import<ImportResultFilterDataDto1>(filePath, labelingFilePath);
    }

上記コードで指定されたエラーファイルを開くと、バリデーションメッセージが英語に変更されていることが確認できます:

インポートヘッダーフィルター(IImportHeaderFilter)の使用法

インポートヘッダーフィルターは、列名、検証属性、値マッピングリストなどを変更できます。これは動的な列名や検証ロジック、値マッピングの変更に最適です。前と同様に、まずインポートファイルを準備します。

インポートファイルの準備

ダウンロードリンク

Dtoの準備

/// <summary>
/// 学生データのインポートDto
/// IsLabelingError:データエラーの表示有無
/// </summary>
[ExcelImporter(IsLabelingError = true, ImportHeaderFilter = typeof(ImportHeaderFilterTest))]
public class ImportHeaderFilterDataDto1
{
    /// <summary>
    ///     氏名
    /// </summary>
    [ImporterHeader(Name = "氏名", Author = "雪雁")]
    [Required(ErrorMessage = "学生の氏名は必須です")]
    [MaxLength(50, ErrorMessage = "名前が長すぎます。修正してください。")]
    public string Name { get; set; }

    /// <summary>
    ///     性別
    /// </summary>
    [ImporterHeader(Name = "性別")]
    [Required(ErrorMessage = "性別は必須です")]
    public Genders Gender { get; set; }

}

上記コードでは、ImportHeaderFilterプロパティを使用してヘッダーフィルターの型を指定しています。次に、関連実装を進めます:

IImportHeaderFilterインターフェースを実装したクラスの作成

/// <summary>
/// インポートヘッダー・フィルターのテスト
/// 1)ヘッダー名の変更テスト
/// 2)値マッピングの変更テスト
/// </summary>
public class ImportHeaderFilterTest : IImportHeaderFilter
{
    public List<ImporterHeaderInfo> Filter(List<ImporterHeaderInfo> importerHeaderInfos)
    {
        foreach (var item in importerHeaderInfos)
        {
            if (item.PropertyName == "Name")
            {
                item.Header.Name = "Student";
            }
            else if (item.PropertyName == "Gender")
            {
                item.MappingValues = new Dictionary<string, dynamic>()
                {
                    {"男",0 },
                    {"女",1 }
                };
            }
        }
        return importerHeaderInfos;
    }
}

上記コードにより、以下のテストを実行しました:

  1. IImportHeaderFilterインターフェースを実装
  2. プロパティ名が「Name」の列のヘッダーを「Student」に変更
  3. プロパティ名が「Gender」の列のマッピングを「男」「女」から数値に変更

続いて、インポートロジックを記述します:

  public async Task ImportHeaderFilter_Test()
    {
        var filePath = Path.Combine(Directory.GetCurrentDirectory(), "TestFiles", "Import", "インポートヘッダーフィルターテスト.xlsx");
        var import = await Importer.Import<ImportHeaderFilterDataDto1>(filePath);
    }

以下の画像のように、Excelの「Student」という列名がDtoのNameプロパティに正しくマッピングされ、男女が列挙型に変換されました:

エクスポートヘッダーフィルター(IExporterHeaderFilter)の使用法

エクスポートヘッダーフィルターは、列ヘッダー、インデックス、値マッピングを変更できます。これは動的なエクスポートロジックの変更に最適です。例えば、列ヘッダーの日本語と英語の変換や、動的な値マッピングなどに使用できます。以下で実践例を確認します:

Dtoの準備とエクスポートコードの作成

[Exporter(Name = "テスト", TableStyle = "Light10", ExporterHeaderFilter = typeof(TestExporterHeaderFilter1))]
public class ExporterHeaderFilterTestData1
{
    [ExporterHeader(DisplayName = "太字テキスト", IsBold = true)]
    public string Text { get; set; }

    [ExporterHeader(DisplayName = "通常テキスト")] public string Text2 { get; set; }

    [ExporterHeader(DisplayName = "無視", IsIgnore = true)]
    public string Text3 { get; set; }

    [ExporterHeader(DisplayName = "数値", Format = "#,##0")]
    public decimal Number { get; set; }

    [ExporterHeader(DisplayName = "名前", IsAutoFit = true)]
    public string Name { get; set; }
}

上記Dtoコードでは、エクスポート属性ExporterのExporterHeaderFilterプロパティを使用してエクスポートヘッダーフィルターを指定しています。

IExporterHeaderFilterインターフェースの実装

public class TestExporterHeaderFilter1 : IExporterHeaderFilter
{
    /// <summary>
    /// ヘッダー・フィルター(名前の変更)
    /// </summary>
    /// <param name="exporterHeaderInfo"></param>
    /// <returns></returns>
    public ExporterHeaderInfo Filter(ExporterHeaderInfo exporterHeaderInfo)
    {
        if (exporterHeaderInfo.DisplayName.Equals("名前"))
        {
            exporterHeaderInfo.DisplayName = "name";
        }
        return exporterHeaderInfo;
    }
}

上記コードでは、エクスポートフィルターを実装し、「名前」という表示名を持つ列を「name」に変更しています。

エクスポートロジックの記述

//エクスポート
IExporter exporter = new ExcelExporter();
//GenFuを使用してテストデータを生成
var data1 = GenFu.GenFu.ListOf<ExporterHeaderFilterTestData1>();
var result = await exporter.Export(filePath, data1);

上記コードでエクスポートした後、結果を確認します:

簡単ですよね?他の処理も可能です。たとえば、無視列を変更する方法:

public class TestExporterHeaderFilter2 : IExporterHeaderFilter
{
    /// <summary>
    /// ヘッダー・フィルター(無視列の変更)
    /// </summary>
    /// <param name="exporterHeaderInfo"></param>
    /// <returns></returns>
    public ExporterHeaderInfo Filter(ExporterHeaderInfo exporterHeaderInfo)
    {
        if (exporterHeaderInfo.ExporterHeaderAttribute.IsIgnore)
        {
            exporterHeaderInfo.ExporterHeaderAttribute.IsIgnore = false;
        }
        return exporterHeaderInfo;
    }
}

コンテナ依存性注入によるフィルターの使用方法

フィルターは、インポート・エクスポート時に動的に処理を行うためのものです。例えば、値マッピングなどです。しかし、属性でフィルターを指定する場合、依存性注入はどうやって対応すればよいでしょうか?心配いりません。このシナリオに対応するための仕組みがあります。

ASP.NET CoreのStartupクラスでのコンテナ登録

参考コードは以下の通りです:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    AppDependencyResolver.Init(app.ApplicationServices);
    //依存関係の登録
    services.AddSingleton<IImportResultFilter, ImportResultFilterTest>();
    services.AddSingleton<IImportHeaderFilter, ImportHeaderFilterTest>();
    services.AddSingleton<IExporterHeaderFilter, TestExporterHeaderFilter1>();    
}

これで、自由に利用できます。注意すべき点は:

  1. 依存注入されたフィルターの優先順位は、属性で指定されたものより高い。両者が同時に存在する場合、注入されたものが優先されます。
  2. 注入されたフィルターはグローバルです。複数の型のフィルターを注入した場合、すべて実行されます。今後、より詳細な制御機能も追加予定です。
  3. 特定のロジックで全てのフィルターを無効化したい場合は、下記のセクションを参照してください。
  4. この機能は2.4.0-beta2以降のバージョンで利用可能です。

IsDisableAllFilterプロパティで全フィルターを無効化

特定のインポート・エクスポートですべてのフィルターを無効化したい場合はどうすればよいでしょうか?IsDisableAllFilterをtrueに設定するだけでOKです。インポート・エクスポート属性ともにサポートされています。

タグ: Magicodes.IE インポート エクスポート フィルター ASP.NET Core

6月22日 22:24 投稿