refactor(ExcelIO): migrate ExcelExportService to Core models and FluentTableWriter

- Replace AttributeTableWriter with FluentTableWriter in ExcelExportService
- Inject ExcelMapRegistry for fluent map lookups
- Use registry.GetMap<T>().TabName instead of reflection-based attribute reading
- Update ExcelIO SearchModel to reference Core result types via aliases
- Remove System.Reflection import (no longer needed)
- Add JdeScoping.Core.Models.SearchResults import for result types
This commit is contained in:
Joseph Doherty
2026-01-06 23:38:30 -05:00
parent a6b7f646b1
commit 8883fb2680
2 changed files with 28 additions and 18 deletions
@@ -1,14 +1,16 @@
using System.Reflection;
using ClosedXML.Excel; using ClosedXML.Excel;
using JdeScoping.Core.Interfaces; using JdeScoping.Core.Interfaces;
using JdeScoping.ExcelIO.Attributes; using JdeScoping.Core.Models.SearchResults;
using JdeScoping.ExcelIO.Options; using JdeScoping.ExcelIO.Options;
using JdeScoping.ExcelIO.Formatting; using JdeScoping.ExcelIO.Formatting;
using JdeScoping.ExcelIO.Generators; using JdeScoping.ExcelIO.Generators;
using JdeScoping.ExcelIO.Models.Reporting; using JdeScoping.ExcelIO.Mapping;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
// Use ExcelIO's SearchModel which contains criteria filter properties for CriteriaSheetGenerator
using ExcelSearchModel = JdeScoping.ExcelIO.Models.Reporting.SearchModel;
namespace JdeScoping.ExcelIO; namespace JdeScoping.ExcelIO;
/// <summary> /// <summary>
@@ -19,7 +21,8 @@ public class ExcelExportService : IExcelExportService
private readonly ILogger<ExcelExportService> _logger; private readonly ILogger<ExcelExportService> _logger;
private readonly IOptions<ExcelExportOptions> _options; private readonly IOptions<ExcelExportOptions> _options;
private readonly CriteriaSheetGenerator _criteriaGenerator; private readonly CriteriaSheetGenerator _criteriaGenerator;
private readonly AttributeTableWriter _tableWriter; private readonly FluentTableWriter _tableWriter;
private readonly ExcelMapRegistry _registry;
/// <summary> /// <summary>
/// Initializes a new instance of the ExcelExportService class. /// Initializes a new instance of the ExcelExportService class.
@@ -27,25 +30,28 @@ public class ExcelExportService : IExcelExportService
/// <param name="logger">Logger instance.</param> /// <param name="logger">Logger instance.</param>
/// <param name="options">Excel export options.</param> /// <param name="options">Excel export options.</param>
/// <param name="criteriaGenerator">Criteria sheet generator.</param> /// <param name="criteriaGenerator">Criteria sheet generator.</param>
/// <param name="tableWriter">Attribute table writer.</param> /// <param name="tableWriter">Fluent table writer.</param>
/// <param name="registry">Excel map registry.</param>
public ExcelExportService( public ExcelExportService(
ILogger<ExcelExportService> logger, ILogger<ExcelExportService> logger,
IOptions<ExcelExportOptions> options, IOptions<ExcelExportOptions> options,
CriteriaSheetGenerator criteriaGenerator, CriteriaSheetGenerator criteriaGenerator,
AttributeTableWriter tableWriter) FluentTableWriter tableWriter,
ExcelMapRegistry registry)
{ {
_logger = logger; _logger = logger;
_options = options; _options = options;
_criteriaGenerator = criteriaGenerator; _criteriaGenerator = criteriaGenerator;
_tableWriter = tableWriter; _tableWriter = tableWriter;
_registry = registry;
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<byte[]> GenerateAsync(object search, CancellationToken cancellationToken = default) public async Task<byte[]> GenerateAsync(object search, CancellationToken cancellationToken = default)
{ {
if (search is not SearchModel searchModel) if (search is not ExcelSearchModel searchModel)
{ {
throw new ArgumentException($"Expected {nameof(SearchModel)} but received {search.GetType().Name}", nameof(search)); throw new ArgumentException($"Expected {nameof(ExcelSearchModel)} but received {search.GetType().Name}", nameof(search));
} }
return await GenerateAsync(searchModel, cancellationToken); return await GenerateAsync(searchModel, cancellationToken);
@@ -57,7 +63,7 @@ public class ExcelExportService : IExcelExportService
/// <param name="search">Search model with criteria and results.</param> /// <param name="search">Search model with criteria and results.</param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Excel file as byte array.</returns> /// <returns>Excel file as byte array.</returns>
public async Task<byte[]> GenerateAsync(SearchModel search, CancellationToken cancellationToken = default) public async Task<byte[]> GenerateAsync(ExcelSearchModel search, CancellationToken cancellationToken = default)
{ {
using var scope = _logger.BeginScope(new Dictionary<string, object> using var scope = _logger.BeginScope(new Dictionary<string, object>
{ {
@@ -128,8 +134,8 @@ public class ExcelExportService : IExcelExportService
private void GenerateResultsSheet(XLWorkbook workbook, List<SearchResult> results) private void GenerateResultsSheet(XLWorkbook workbook, List<SearchResult> results)
{ {
var tableAttr = typeof(SearchResult).GetCustomAttribute<OutputTableAttribute>(); var map = _registry.GetMap<SearchResult>();
var tabName = tableAttr?.TabName ?? "Search Results"; var tabName = map.TabName ?? "Search Results";
var worksheet = workbook.Worksheets.Add(tabName); var worksheet = workbook.Worksheets.Add(tabName);
var table = _tableWriter.WriteTable(worksheet, 1, 1, results); var table = _tableWriter.WriteTable(worksheet, 1, 1, results);
@@ -147,8 +153,8 @@ public class ExcelExportService : IExcelExportService
private void GenerateMisInfoSheet(XLWorkbook workbook, List<MisSearchResult> misResults) private void GenerateMisInfoSheet(XLWorkbook workbook, List<MisSearchResult> misResults)
{ {
var tableAttr = typeof(MisSearchResult).GetCustomAttribute<OutputTableAttribute>(); var map = _registry.GetMap<MisSearchResult>();
var tabName = tableAttr?.TabName ?? "MIS Info"; var tabName = map.TabName ?? "MIS Info";
var worksheet = workbook.Worksheets.Add(tabName); var worksheet = workbook.Worksheets.Add(tabName);
var table = _tableWriter.WriteTable(worksheet, 1, 1, misResults); var table = _tableWriter.WriteTable(worksheet, 1, 1, misResults);
@@ -166,8 +172,8 @@ public class ExcelExportService : IExcelExportService
private void GenerateInvestigationSheet(XLWorkbook workbook, List<MisNonMatchSearchResult> misNonMatchResults) private void GenerateInvestigationSheet(XLWorkbook workbook, List<MisNonMatchSearchResult> misNonMatchResults)
{ {
var tableAttr = typeof(MisNonMatchSearchResult).GetCustomAttribute<OutputTableAttribute>(); var map = _registry.GetMap<MisNonMatchSearchResult>();
var tabName = tableAttr?.TabName ?? "Investigation"; var tabName = map.TabName ?? "Investigation";
var worksheet = workbook.Worksheets.Add(tabName); var worksheet = workbook.Worksheets.Add(tabName);
var table = _tableWriter.WriteTable(worksheet, 1, 1, misNonMatchResults); var table = _tableWriter.WriteTable(worksheet, 1, 1, misNonMatchResults);
@@ -1,3 +1,7 @@
using CoreSearchResult = JdeScoping.Core.Models.SearchResults.SearchResult;
using CoreMisSearchResult = JdeScoping.Core.Models.SearchResults.MisSearchResult;
using CoreMisNonMatchSearchResult = JdeScoping.Core.Models.SearchResults.MisNonMatchSearchResult;
namespace JdeScoping.ExcelIO.Models.Reporting; namespace JdeScoping.ExcelIO.Models.Reporting;
/// <summary> /// <summary>
@@ -128,15 +132,15 @@ public class SearchModel
/// <summary> /// <summary>
/// Work order search results. /// Work order search results.
/// </summary> /// </summary>
public List<SearchResult> Results { get; set; } = []; public List<CoreSearchResult> Results { get; set; } = [];
/// <summary> /// <summary>
/// MIS results. /// MIS results.
/// </summary> /// </summary>
public List<MisSearchResult>? MisResults { get; set; } public List<CoreMisSearchResult>? MisResults { get; set; }
/// <summary> /// <summary>
/// MIS no match found results. /// MIS no match found results.
/// </summary> /// </summary>
public List<MisNonMatchSearchResult>? MisNonMatchResults { get; set; } public List<CoreMisNonMatchSearchResult>? MisNonMatchResults { get; set; }
} }