diff --git a/MiniExcel.slnx b/MiniExcel.slnx index afe06bcc..a34ae0b2 100644 --- a/MiniExcel.slnx +++ b/MiniExcel.slnx @@ -1,40 +1,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj b/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj index 246a2f8e..108cf0d2 100644 --- a/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj +++ b/benchmarks/MiniExcel.Benchmarks/MiniExcel.Benchmarks.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/MiniExcel.OpenXml/FluentMapping/Api/MappingExporter.cs b/src/MiniExcel.OpenXml.FluentMapping/Api/MappingExporter.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Api/MappingExporter.cs rename to src/MiniExcel.OpenXml.FluentMapping/Api/MappingExporter.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Api/MappingImporter.cs b/src/MiniExcel.OpenXml.FluentMapping/Api/MappingImporter.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Api/MappingImporter.cs rename to src/MiniExcel.OpenXml.FluentMapping/Api/MappingImporter.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Api/MappingTemplater.cs b/src/MiniExcel.OpenXml.FluentMapping/Api/MappingTemplater.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Api/MappingTemplater.cs rename to src/MiniExcel.OpenXml.FluentMapping/Api/MappingTemplater.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Api/ProviderExtensions.cs b/src/MiniExcel.OpenXml.FluentMapping/Api/ProviderExtensions.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Api/ProviderExtensions.cs rename to src/MiniExcel.OpenXml.FluentMapping/Api/ProviderExtensions.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/CompiledMapping.cs b/src/MiniExcel.OpenXml.FluentMapping/CompiledMapping.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/CompiledMapping.cs rename to src/MiniExcel.OpenXml.FluentMapping/CompiledMapping.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/CollectionMappingBuilder.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/CollectionMappingBuilder.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/CollectionMappingBuilder.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/CollectionMappingBuilder.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/ICollectionMappingBuilder.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/ICollectionMappingBuilder.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/ICollectionMappingBuilder.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/ICollectionMappingBuilder.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/IMappingConfiguration.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/IMappingConfiguration.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/IMappingConfiguration.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/IMappingConfiguration.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/IPropertyMappingBuilder.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/IPropertyMappingBuilder.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/IPropertyMappingBuilder.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/IPropertyMappingBuilder.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/MappingConfiguration.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/MappingConfiguration.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/MappingConfiguration.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/MappingConfiguration.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/Configuration/PropertyMappingBuilder.cs b/src/MiniExcel.OpenXml.FluentMapping/Configuration/PropertyMappingBuilder.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Configuration/PropertyMappingBuilder.cs rename to src/MiniExcel.OpenXml.FluentMapping/Configuration/PropertyMappingBuilder.cs diff --git a/src/MiniExcel.OpenXml.FluentMapping/GlobalUsings.cs b/src/MiniExcel.OpenXml.FluentMapping/GlobalUsings.cs new file mode 100644 index 00000000..29ebd83c --- /dev/null +++ b/src/MiniExcel.OpenXml.FluentMapping/GlobalUsings.cs @@ -0,0 +1,20 @@ +// Global using directives + +global using System.Collections; +global using System.Globalization; +global using System.IO.Compression; +global using System.Linq.Expressions; +global using System.Reflection; +global using System.Runtime.CompilerServices; +global using System.Text; +global using System.Text.RegularExpressions; +global using System.Xml; +global using MiniExcelLib.Core; +global using MiniExcelLib.Core.Abstractions; +global using MiniExcelLib.Core.Helpers; +global using MiniExcelLib.Core.Reflection; +global using MiniExcelLib.OpenXml.Constants; +global using MiniExcelLib.OpenXml.Helpers; +global using MiniExcelLib.OpenXml.Models; +global using MiniExcelLib.OpenXml.Utils; +global using Zomp.SyncMethodGenerator; diff --git a/src/MiniExcel.OpenXml/FluentMapping/Helpers/ConversionHelper.cs b/src/MiniExcel.OpenXml.FluentMapping/Helpers/ConversionHelper.cs similarity index 94% rename from src/MiniExcel.OpenXml/FluentMapping/Helpers/ConversionHelper.cs rename to src/MiniExcel.OpenXml.FluentMapping/Helpers/ConversionHelper.cs index a0838cd4..e1e98375 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/Helpers/ConversionHelper.cs +++ b/src/MiniExcel.OpenXml.FluentMapping/Helpers/ConversionHelper.cs @@ -10,8 +10,15 @@ internal static class ConversionHelper // Cache compiled conversion delegates private static readonly ConcurrentDictionary<(Type Source, Type Target), Func> ConversionCache = new(); - public static object? ConvertValue(object value, Type targetType, string? format = null) + public static object? ConvertValue(object? value, Type targetType, string? format = null) { + if (value is null) + { + return targetType.IsValueType && Nullable.GetUnderlyingType(targetType) is null + ? Activator.CreateInstance(targetType) + : null; + } + var sourceType = value.GetType(); // Fast path: no conversion needed diff --git a/src/MiniExcel.OpenXml/FluentMapping/Helpers/MappingMetadataExtractor.cs b/src/MiniExcel.OpenXml.FluentMapping/Helpers/MappingMetadataExtractor.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/Helpers/MappingMetadataExtractor.cs rename to src/MiniExcel.OpenXml.FluentMapping/Helpers/MappingMetadataExtractor.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappedRow.cs b/src/MiniExcel.OpenXml.FluentMapping/MappedRow.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappedRow.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappedRow.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingCellStream.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingCellStream.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappingCellStream.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingCellStream.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingCellStreamAdapter.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingCellStreamAdapter.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingCompiler.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingCompiler.cs similarity index 97% rename from src/MiniExcel.OpenXml/FluentMapping/MappingCompiler.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingCompiler.cs index f6f36c15..4defdfd2 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/MappingCompiler.cs +++ b/src/MiniExcel.OpenXml.FluentMapping/MappingCompiler.cs @@ -53,7 +53,8 @@ public static CompiledMapping Compile(MappingConfiguration? configurati if (prop.CellAddress is null) continue; - CellReferenceConverter.TryParseCellReference(prop.CellAddress, out int cellCol, out int cellRow); + if (!CellReferenceConverter.TryParseCellReference(prop.CellAddress, out int cellCol, out int cellRow)) + throw new InvalidOperationException($"Invalid cell address '{prop.CellAddress}' for property '{propertyName}'."); properties.Add(new CompiledPropertyMapping { diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingReader.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingReader.cs similarity index 97% rename from src/MiniExcel.OpenXml/FluentMapping/MappingReader.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingReader.cs index 1725adc1..9d3e9b29 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/MappingReader.cs +++ b/src/MiniExcel.OpenXml.FluentMapping/MappingReader.cs @@ -26,11 +26,12 @@ private static async IAsyncEnumerable QueryOptimizedAsync(Stream stream, Comp var cellGrid = mapping.OptimizedCellGrid!; // Read the Excel file using OpenXmlReader's direct mapping path - using var reader = await OpenXmlReader.CreateAsync(stream, new OpenXmlConfiguration + var reader = await OpenXmlMappingReader.CreateAsync(stream, new OpenXmlConfiguration { FillMergedCells = false, FastMode = false }, leaveOpen, cancellationToken).ConfigureAwait(false); + await using var disposableReader = reader.ConfigureAwait(false); // If we have collections, we need to handle multiple items with collections if (mapping.Collections.Any()) diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingRegistry.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingRegistry.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingTemplateApplicator.cs similarity index 96% rename from src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingTemplateApplicator.cs index 2936994f..52e73284 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateApplicator.cs +++ b/src/MiniExcel.OpenXml.FluentMapping/MappingTemplateApplicator.cs @@ -23,7 +23,7 @@ public static async Task ApplyTemplateAsync( if (!templateStream.CanSeek) { // Copy to memory stream if not seekable - var memStream = new MemoryStream(); + using var memStream = new MemoryStream(); await templateStream.CopyToAsync(memStream #if NET , cancellationToken diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingTemplateProcessor.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingTemplateProcessor.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappingTemplateProcessor.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingTemplateProcessor.cs diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingWriter.cs b/src/MiniExcel.OpenXml.FluentMapping/MappingWriter.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/MappingWriter.cs rename to src/MiniExcel.OpenXml.FluentMapping/MappingWriter.cs diff --git a/src/MiniExcel.OpenXml.FluentMapping/MiniExcel.OpenXml.FluentMapping.csproj b/src/MiniExcel.OpenXml.FluentMapping/MiniExcel.OpenXml.FluentMapping.csproj new file mode 100644 index 00000000..3a22e565 --- /dev/null +++ b/src/MiniExcel.OpenXml.FluentMapping/MiniExcel.OpenXml.FluentMapping.csproj @@ -0,0 +1,20 @@ + + + + MiniExcelLib.OpenXml.FluentMapping + + + + MiniExcel.OpenXml.FluentMapping + MiniExcel.OpenXml.FluentMapping + + + + + + + + + + + diff --git a/src/MiniExcel.OpenXml/FluentMapping/NestedMappingInfo.cs b/src/MiniExcel.OpenXml.FluentMapping/NestedMappingInfo.cs similarity index 100% rename from src/MiniExcel.OpenXml/FluentMapping/NestedMappingInfo.cs rename to src/MiniExcel.OpenXml.FluentMapping/NestedMappingInfo.cs diff --git a/src/MiniExcel.OpenXml/Reader/OpenXmlReader.Mapped.cs b/src/MiniExcel.OpenXml.FluentMapping/OpenXmlMappingReader.cs similarity index 74% rename from src/MiniExcel.OpenXml/Reader/OpenXmlReader.Mapped.cs rename to src/MiniExcel.OpenXml.FluentMapping/OpenXmlMappingReader.cs index c27dd830..d2880654 100644 --- a/src/MiniExcel.OpenXml/Reader/OpenXmlReader.Mapped.cs +++ b/src/MiniExcel.OpenXml.FluentMapping/OpenXmlMappingReader.cs @@ -1,10 +1,48 @@ -using MiniExcelLib.OpenXml.FluentMapping; +using MiniExcelLib.OpenXml.Reader; using MiniExcelLib.OpenXml.Styles; -namespace MiniExcelLib.OpenXml.Reader; +namespace MiniExcelLib.OpenXml.FluentMapping; -internal partial class OpenXmlReader +internal partial class OpenXmlMappingReader(OpenXmlZip archive, IMiniExcelConfiguration? configuration) : OpenXmlReader(archive, configuration) { + [CreateSyncVersion] + internal new static async Task CreateAsync(Stream stream, IMiniExcelConfiguration? configuration, bool leaveOpen = false, CancellationToken cancellationToken = default) + { + OpenXmlZip? archive = null; + OpenXmlMappingReader? reader = null; + + try + { + ThrowHelper.ThrowIfInvalidOpenXml(stream); + + archive = await OpenXmlZip.CreateAsync(stream, leaveOpen: leaveOpen, cancellationToken: cancellationToken).ConfigureAwait(false); + reader = new OpenXmlMappingReader(archive, configuration); + await reader.SetSharedStringsAsync(cancellationToken).ConfigureAwait(false); + + var result = reader; + reader = null; + archive = null; + stream = null!; + + return result; + } + finally + { +#if SYNC_ONLY + reader?.Dispose(); +#else + if (reader?.DisposeAsync() is { } disposeTask) + await disposeTask.ConfigureAwait(false); +#endif + + if (archive is not null) + await archive.DisposeAsync().ConfigureAwait(false); + + if (!leaveOpen && (Stream?)stream is not null) + await stream.DisposeAsync().ConfigureAwait(false); + } + } + /// /// Direct mapped query that bypasses dictionary creation for better performance /// diff --git a/src/MiniExcel.OpenXml/MiniExcel.OpenXml.csproj b/src/MiniExcel.OpenXml/MiniExcel.OpenXml.csproj index 01eb701a..f0b6a087 100644 --- a/src/MiniExcel.OpenXml/MiniExcel.OpenXml.csproj +++ b/src/MiniExcel.OpenXml/MiniExcel.OpenXml.csproj @@ -15,6 +15,11 @@ + + + + + diff --git a/src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs b/src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs index c5bb8996..1f9aa619 100644 --- a/src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs +++ b/src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs @@ -5,20 +5,21 @@ namespace MiniExcelLib.OpenXml.Reader; -internal sealed partial class OpenXmlReader : IMiniExcelReader +internal partial class OpenXmlReader : IMiniExcelReader { - private static readonly string[] Ns = [Schemas.SpreadsheetmlXmlMain, Schemas.SpreadsheetmlXmlStrictNs]; private static readonly string[] RelationshiopNs = [Schemas.SpreadsheetmlXmlRelationships, Schemas.SpreadsheetmlXmlStrictRelationships]; - private readonly OpenXmlConfiguration _config; + + protected static readonly string[] Ns = [Schemas.SpreadsheetmlXmlMain, Schemas.SpreadsheetmlXmlStrictNs]; + protected readonly OpenXmlConfiguration _config; + protected OpenXmlStyles? _style; private List? _sheetRecords; - private OpenXmlStyles? _style; private bool _disposed; internal readonly OpenXmlZip Archive; internal IDictionary SharedStrings = new Dictionary(); - private OpenXmlReader(OpenXmlZip archive, IMiniExcelConfiguration? configuration) + protected OpenXmlReader(OpenXmlZip archive, IMiniExcelConfiguration? configuration) { Archive = archive; _config = (OpenXmlConfiguration?)configuration ?? OpenXmlConfiguration.Default; @@ -362,7 +363,7 @@ internal static async Task CreateAsync(Stream stream, IMiniExcelC yield return cell; } - private ZipArchiveEntry GetSheetEntry(string? sheetName) + protected ZipArchiveEntry GetSheetEntry(string? sheetName) { // if sheets count > 1 need to read xl/_rels/workbook.xml.rels var sheets = Archive.EntryCollection @@ -429,7 +430,7 @@ private static void SetCellsValueAndHeaders(object? cellValue, bool hasHeaderRow } [CreateSyncVersion] - private async Task SetSharedStringsAsync(CancellationToken cancellationToken = default) + protected async Task SetSharedStringsAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -606,7 +607,7 @@ internal class CellAndColumn(object? cellValue, int columnIndex) } [CreateSyncVersion] - private async Task ReadCellAndSetColumnIndexAsync(XmlReader reader, int columnIndex, bool withoutCr, int startColumnIndex, string aR, string aT, CancellationToken cancellationToken = default) + protected async Task ReadCellAndSetColumnIndexAsync(XmlReader reader, int columnIndex, bool withoutCr, int startColumnIndex, string aR, string aT, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/MiniExcel/MiniExcel.csproj b/src/MiniExcel/MiniExcel.csproj index cee79d8b..5768ec5e 100644 --- a/src/MiniExcel/MiniExcel.csproj +++ b/src/MiniExcel/MiniExcel.csproj @@ -13,6 +13,7 @@ + diff --git a/tests/MiniExcel.OpenXml.Tests/MiniExcel.OpenXml.Tests.csproj b/tests/MiniExcel.OpenXml.Tests/MiniExcel.OpenXml.Tests.csproj index 0480ed72..c6967037 100644 --- a/tests/MiniExcel.OpenXml.Tests/MiniExcel.OpenXml.Tests.csproj +++ b/tests/MiniExcel.OpenXml.Tests/MiniExcel.OpenXml.Tests.csproj @@ -21,9 +21,9 @@ + - runtime; build; native; contentfiles; analyzers; buildtransitive @@ -34,6 +34,7 @@ +