diff --git a/SharpExcel.Models/Attributes/ExcelColumnDefinitionAttribute.cs b/SharpExcel.Models/Attributes/ExcelColumnDefinitionAttribute.cs index f7ac633..b1a3a62 100644 --- a/SharpExcel.Models/Attributes/ExcelColumnDefinitionAttribute.cs +++ b/SharpExcel.Models/Attributes/ExcelColumnDefinitionAttribute.cs @@ -9,9 +9,9 @@ namespace SharpExcel.Models.Attributes; public class ExcelColumnDefinitionAttribute(string columnName, int width = -1, string? format = null) : Attribute { - public string DisplayName { get; set; } = columnName; + public string DisplayName { get; } = columnName; - public int ColumnWidth { get; set; } = width; + public int ColumnWidth { get; } = width; - public string? Format { get; set; } = format; + public string? Format { get; } = format; } \ No newline at end of file diff --git a/SharpExcel.Models/Configuration/ExporterOptions.cs b/SharpExcel.Models/Configuration/ExporterOptions.cs index 80002e9..9790a96 100644 --- a/SharpExcel.Models/Configuration/ExporterOptions.cs +++ b/SharpExcel.Models/Configuration/ExporterOptions.cs @@ -70,7 +70,7 @@ public ExporterOptions WithStylingRule(Action /// Fluent method to add a styling rule for this exporter /// - /// constructs the styling rule + /// constructs targeting rule /// public ExporterOptions WithTarget(Action> targetingRuleOptions) { diff --git a/SharpExcel.Models/Data/TargetingRule.cs b/SharpExcel.Models/Data/TargetingRule.cs index 51bcf6f..015f745 100644 --- a/SharpExcel.Models/Data/TargetingRule.cs +++ b/SharpExcel.Models/Data/TargetingRule.cs @@ -12,7 +12,7 @@ public record TargetingRule : ITargetingRule /// REQUIRED: name of the sheet in the excel file /// [MinLength(1)] - public string SheetName { get; set; } = null!; + public string? SheetName { get; set; } /// /// Optional Row to start reading/writing from. diff --git a/SharpExcel/Exporters/BaseSharpExcelSynchronizer.cs b/SharpExcel/Exporters/BaseSharpExcelSynchronizer.cs index 0fb44d6..76afb07 100644 --- a/SharpExcel/Exporters/BaseSharpExcelSynchronizer.cs +++ b/SharpExcel/Exporters/BaseSharpExcelSynchronizer.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Globalization; using ClosedXML.Excel; -using DocumentFormat.OpenXml.Spreadsheet; using Microsoft.Extensions.Options; using SharpExcel.Abstraction; using SharpExcel.Extensions; @@ -38,18 +37,26 @@ public Task> ReadWorkbookAsync(CultureInfo cultureInfo, var output = new ExcelReadResult(); var instanceData = CreateReadInstanceData(cultureInfo, workbook); - var rules = _options.Value.Targeting.Rules.GroupBy(rule => rule.SheetName); + var rules = _options.Value.Targeting.Rules.Where(rule => rule.SheetName is not null).GroupBy(rule => rule.SheetName); foreach (var ruleGroup in rules) { - if (!instanceData.Workbook.Worksheets.TryGetWorksheet(ruleGroup.Key, out var worksheet)) + if (!instanceData.Workbook.Worksheets.TryGetWorksheet(ruleGroup.Key!, out var worksheet)) { continue; } foreach (var rule in ruleGroup) { - ReadSheetAsync(rule, output, instanceData, worksheet); + ReadSheet(rule, output, instanceData, worksheet); + } + } + + if (workbook.Worksheets.Count > 0) + { + foreach (var rule in _options.Value.Targeting.Rules.Where(rule => rule.SheetName is null)) + { + ReadSheet(rule, output, instanceData, workbook.Worksheets.First()); } } @@ -67,12 +74,19 @@ public virtual async Task GenerateWorkbookAsync(CultureInfo cultureI Dictionary, IEnumerable> dataGroupedByTargetingRule = new(); + if (!workbook.Worksheets.TryGetWorksheet(ExcelTargetingConstants.DefaultTargetingRule.SheetName!, out _)) + { + //create default sheet if it does not exist + workbook.Worksheets.Add(ExcelTargetingConstants.DefaultTargetingRule.SheetName!); + } + foreach (var targetingRule in _options.Value.Targeting.Rules) { dataGroupedByTargetingRule.Add(targetingRule, data.Where(x => targetingRule.RulePredicate != null && targetingRule.RulePredicate(x)).ToList()); - if (!workbook.Worksheets.TryGetWorksheet(targetingRule.SheetName, out var _)) + + if (targetingRule?.SheetName is not null && !workbook.Worksheets.TryGetWorksheet(targetingRule.SheetName, out _)) { - workbook.Worksheets.Add(targetingRule.SheetName); + workbook.Worksheets.Add((targetingRule?.SheetName ?? ExcelTargetingConstants.DefaultTargetingRule.SheetName)!); } } @@ -89,12 +103,11 @@ public virtual async Task GenerateWorkbookAsync(CultureInfo cultureI } - /// internal virtual Task GenerateSheetAsync(TargetingRule targetingRule, SharpExcelWriterInstanceData instanceData, IEnumerable data) { - if (!instanceData.Workbook.Worksheets.TryGetWorksheet(targetingRule.SheetName, out var _)) + if (!instanceData.Workbook.Worksheets.TryGetWorksheet((targetingRule.SheetName ?? ExcelTargetingConstants.DefaultTargetingRule.SheetName)!, out _)) { - instanceData.Workbook.Worksheets.Add(targetingRule.SheetName); + instanceData.Workbook.Worksheets.Add(targetingRule.SheetName ?? ExcelTargetingConstants.DefaultTargetingRule.SheetName!); } //start at Row 1 if not defined because Excel starts at 1 @@ -114,8 +127,7 @@ internal virtual Task GenerateSheetAsync(TargetingRule targetingRule, Sh return Task.CompletedTask; } - /// - internal void ReadSheetAsync(TargetingRule rule, ExcelReadResult result, SharpExcelWriterInstanceData instanceData, IXLWorksheet worksheet) + internal void ReadSheet(TargetingRule rule, ExcelReadResult result, SharpExcelWriterInstanceData instanceData, IXLWorksheet worksheet) { var usedArea = worksheet.RangeUsed(); if (usedArea is null) @@ -131,14 +143,6 @@ internal void ReadSheetAsync(TargetingRule rule, ExcelReadResult { var data = ReadRow(worksheet, instanceData, row.WorksheetRow(), out var validationResults); - if (data == null) - { - //skip to next record if we can't read record - continue; - } - - result.Records.Add(data); - //add validation results if (validationResults.Any()) { @@ -150,8 +154,11 @@ internal void ReadSheetAsync(TargetingRule rule, ExcelReadResult ValidationResults = validationResult.Value }); } + + continue; } - + + result.Records.Add(data); } } @@ -163,7 +170,7 @@ internal void ReadSheetAsync(TargetingRule rule, ExcelReadResult /// row to read /// A dictionary containing validation results of previous rows /// - private static TModel? ReadRow( + private static TModel ReadRow( IXLWorksheet sheet, SharpExcelWriterInstanceData instance, IXLRow row, @@ -212,9 +219,9 @@ internal void ReadSheetAsync(TargetingRule rule, ExcelReadResult /// /// Finds header row and maps the column order so we can fill them later /// + /// /// instance data /// total used area of the workbook - /// /// private static int FindAndMapHeaderRow( TargetingRule rule, @@ -243,7 +250,7 @@ private static int FindAndMapHeaderRow( for (int i = startIndex; i <= usedArea.ColumnCount(); i++) { - var cell = headerRow!.Cell(i); + var cell = headerRow.Cell(i); if (!cell.TryGetValue(out string cellValue)) continue; @@ -265,12 +272,6 @@ private static int FindAndMapHeaderRow( return headerRowId <= 1 ? 2 : headerRowId - 1; } - /// - /// Creates lookup data for the current export run - /// - /// arguments to use - /// workbook to use - /// private SharpExcelWriterInstanceData CreateWriteInstanceData(CultureInfo cultureInfo, XLWorkbook workbook) { var random = new Random(); @@ -291,12 +292,6 @@ private SharpExcelWriterInstanceData CreateWriteInstanceData(CultureInfo return run; } - /// - /// Creates lookup data for the current import run - /// - /// arguments to use - /// workbook to use - /// private SharpExcelWriterInstanceData CreateReadInstanceData(CultureInfo cultureInfo, XLWorkbook workbook) { return new SharpExcelWriterInstanceData()