全体設計
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;
}
}
上記コードにより、以下のテストを実行しました:
- IImportHeaderFilterインターフェースを実装
- プロパティ名が「Name」の列のヘッダーを「Student」に変更
- プロパティ名が「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>();
}
これで、自由に利用できます。注意すべき点は:
- 依存注入されたフィルターの優先順位は、属性で指定されたものより高い。両者が同時に存在する場合、注入されたものが優先されます。
- 注入されたフィルターはグローバルです。複数の型のフィルターを注入した場合、すべて実行されます。今後、より詳細な制御機能も追加予定です。
- 特定のロジックで全てのフィルターを無効化したい場合は、下記のセクションを参照してください。
- この機能は2.4.0-beta2以降のバージョンで利用可能です。
IsDisableAllFilterプロパティで全フィルターを無効化
特定のインポート・エクスポートですべてのフィルターを無効化したい場合はどうすればよいでしょうか?IsDisableAllFilterをtrueに設定するだけでOKです。インポート・エクスポート属性ともにサポートされています。