diff --git a/MiniExcel.slnx b/MiniExcel.slnx index 989be6cc..73698bc3 100644 --- a/MiniExcel.slnx +++ b/MiniExcel.slnx @@ -25,6 +25,7 @@ + diff --git a/src/.editorconfig b/src/.editorconfig index a226b8bb..68647853 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -6,6 +6,7 @@ dotnet_diagnostic.CA1835.severity = error # CA1849: Call async methods when in an async method dotnet_diagnostic.CA1849.severity = error +# CA2000: Dispose objects before losing scope dotnet_diagnostic.CA2000.severity = error # CA2007: Do not directly await a Task diff --git a/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapter.cs b/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapter.cs index 7c0784ef..85408fc1 100644 --- a/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapter.cs +++ b/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapter.cs @@ -4,12 +4,12 @@ public interface IMiniExcelWriteAdapter { bool TryGetKnownCount(out int count); List? GetColumns(); - IEnumerable> GetRows(List props, CancellationToken cancellationToken = default); + IEnumerable GetRows(List mappings, CancellationToken cancellationToken = default); } -public readonly struct CellWriteInfo(object? value, int cellIndex, MiniExcelColumnMapping prop) +public readonly struct CellWriteInfo(object? value, int cellIndex, MiniExcelColumnMapping? mapping) { public object? Value { get; } = value; public int CellIndex { get; } = cellIndex; - public MiniExcelColumnMapping Prop { get; } = prop; -} \ No newline at end of file + public MiniExcelColumnMapping? Mapping { get; } = mapping; +} diff --git a/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapterAsync.cs b/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapterAsync.cs index 2e88ec26..fb754da8 100644 --- a/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapterAsync.cs +++ b/src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapterAsync.cs @@ -3,5 +3,5 @@ public interface IMiniExcelWriteAdapterAsync { Task?> GetColumnsAsync(); - IAsyncEnumerable GetRowsAsync(List props, CancellationToken cancellationToken); + IAsyncEnumerable GetRowsAsync(List mappings, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/src/MiniExcel.Core/Helpers/NetSatandardExtensions.cs b/src/MiniExcel.Core/Helpers/NetSatandardExtensions.cs new file mode 100644 index 00000000..50d412a9 --- /dev/null +++ b/src/MiniExcel.Core/Helpers/NetSatandardExtensions.cs @@ -0,0 +1,11 @@ +namespace MiniExcelLib.Core.Helpers; + +public static class NetStandardExtensions +{ +#if NETSTANDARD2_0 + public static TValue? GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key, TValue? defaultValue = default) + { + return dictionary.TryGetValue(key, out var value) ? value : defaultValue; + } +#endif +} diff --git a/src/MiniExcel.Core/Reflection/ColumnMappingsProvider.cs b/src/MiniExcel.Core/Reflection/ColumnMappingsProvider.cs index 1e0d72b5..5a9f1e72 100644 --- a/src/MiniExcel.Core/Reflection/ColumnMappingsProvider.cs +++ b/src/MiniExcel.Core/Reflection/ColumnMappingsProvider.cs @@ -48,14 +48,14 @@ internal static List GetMappingsForImport(Type type, str private static List GetMappingsForExport(this Type type, MiniExcelBaseConfiguration configuration) { - var props = GetColumnMappings(type, ExportMembersFlags, configuration) + var mappings = GetColumnMappings(type, ExportMembersFlags, configuration) .Where(prop => prop?.MemberAccessor.CanRead is true) .ToList(); - if (props.Count == 0) + if (mappings.Count == 0) throw new InvalidMappingException($"{type.Name} must contain at least one mappable property or field.", type); - return SortMappings(props); + return SortMappings(mappings); } private static List SortMappings(List mappings) @@ -81,29 +81,29 @@ internal static List GetMappingsForImport(Type type, str if (explicitIndexMappings.Count != 0) maxColumnIndex = Math.Max(explicitIndexMappings.Max(w => w?.ExcelColumnIndex ?? 0), maxColumnIndex); - var withoutCustomIndexProps = mappings + var mappingsWithoutCustomIndex = mappings .Where(w => w?.ExcelColumnIndex is null or -1) .ToList(); var index = 0; - var newProps = new List(); + var newMappings = new List(); for (int i = 0; i <= maxColumnIndex; i++) { if (explicitIndexMappings.SingleOrDefault(s => s?.ExcelColumnIndex == i) is { } p1) { - newProps.Add(p1); + newMappings.Add(p1); } else { - var p2 = withoutCustomIndexProps.ElementAtOrDefault(index); + var map = mappingsWithoutCustomIndex.ElementAtOrDefault(index); - p2?.ExcelColumnIndex = i; - newProps.Add(p2); + map?.ExcelColumnIndex = i; + newMappings.Add(map); index++; } } - return newProps; + return newMappings; } private static IEnumerable GetColumnMappings(Type type, BindingFlags bindingFlags, MiniExcelBaseConfiguration configuration) @@ -156,7 +156,7 @@ internal static List GetMappingsForImport(Type type, str private static List GetDictionaryColumnInfo(IDictionary? dicString, IDictionary? dic, MiniExcelBaseConfiguration configuration) { - var props = new List(); + var mappings = new List(); var keys = dicString?.Keys.ToList() ?? dic?.Keys @@ -164,15 +164,15 @@ internal static List GetMappingsForImport(Type type, str foreach (var key in keys) { - SetDictionaryColumnInfo(props, key, configuration); + SetDictionaryColumnInfo(mappings, key, configuration); } - return SortMappings(props); + return SortMappings(mappings); } - private static void SetDictionaryColumnInfo(List props, object key, MiniExcelBaseConfiguration configuration) + private static void SetDictionaryColumnInfo(List mappings, object key, MiniExcelBaseConfiguration configuration) { - var mapping = new MiniExcelColumnMapping + var map = new MiniExcelColumnMapping { Key = key, ExcelColumnName = key?.ToString() @@ -185,40 +185,40 @@ private static void SetDictionaryColumnInfo(List props, var dynamicColumn = configuration.DynamicColumns.SingleOrDefault(x => x.Key == key?.ToString()); if (dynamicColumn is not null) { - mapping.Nullable = true; + map.Nullable = true; if (dynamicColumn is { Format: { } fmt, FormatId: var fmtId }) { - mapping.ExcelFormat = fmt; - mapping.ExcelFormatId = fmtId; + map.ExcelFormat = fmt; + map.ExcelFormatId = fmtId; } if (dynamicColumn.Aliases is { } aliases) - mapping.ExcelColumnAliases = aliases; + map.ExcelColumnAliases = aliases; if (dynamicColumn.IndexName is { } idxName) - mapping.ExcelIndexName = idxName; + map.ExcelIndexName = idxName; if (dynamicColumn.Name is { } colName) - mapping.ExcelColumnName = colName; + map.ExcelColumnName = colName; - mapping.ExcelColumnIndex = dynamicColumn.Index; - mapping.ExcelColumnWidth = dynamicColumn.Width; - mapping.ExcelHiddenColumn = dynamicColumn.Hidden; - mapping.ExcelColumnType = dynamicColumn.Type; - mapping.CustomFormatter = dynamicColumn.CustomFormatter; + map.ExcelColumnIndex = dynamicColumn.Index; + map.ExcelColumnWidth = dynamicColumn.Width; + map.ExcelHiddenColumn = dynamicColumn.Hidden; + map.ExcelColumnType = dynamicColumn.Type; + map.CustomFormatter = dynamicColumn.CustomFormatter; isIgnore = dynamicColumn.Ignore; } } if (!isIgnore) - props.Add(mapping); + mappings.Add(map); } - internal static bool TryGetColumnMappings(Type? type, MiniExcelBaseConfiguration configuration, out List props) + internal static bool TryGetColumnMappings(Type? type, MiniExcelBaseConfiguration configuration, out List mappings) { - props = []; + mappings = []; // Unknown type if (type is null) @@ -230,7 +230,7 @@ internal static bool TryGetColumnMappings(Type? type, MiniExcelBaseConfiguration if (ValueIsNeededToDetermineProperties(type)) return false; - props = type.GetMappingsForExport(configuration); + mappings = type.GetMappingsForExport(configuration); return true; } diff --git a/src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs b/src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs index d9665b09..32ed1509 100644 --- a/src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs +++ b/src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs @@ -27,7 +27,7 @@ internal sealed class AsyncEnumerableWriteAdapter(IAsyncEnumerable values, return ColumnMappingsProvider.GetColumnMappingFromValue(_enumerator.Current, _configuration); } - public async IAsyncEnumerable GetRowsAsync(List props, [EnumeratorCancellation] CancellationToken cancellationToken) + public async IAsyncEnumerable GetRowsAsync(List mappings, [EnumeratorCancellation] CancellationToken cancellationToken) { if (_empty) yield break; @@ -44,30 +44,27 @@ public async IAsyncEnumerable GetRowsAsync(List props) + private static CellWriteInfo[] GetRowValues(T currentValue, List mappings) { var column = 0; - var result = new List(); + var result = new List(mappings.Count); - foreach (var prop in props) + foreach (var map in mappings) { column++; - - if (prop is null) - continue; - - var info = currentValue switch + var cellValue = currentValue switch { - IDictionary genericDictionary => new CellWriteInfo(genericDictionary[prop.Key.ToString()], column, prop), - IDictionary dictionary => new CellWriteInfo(dictionary[prop.Key], column, prop), - _ => new CellWriteInfo(prop.MemberAccessor.GetValue(currentValue), column, prop) + _ when map is null => null, + IDictionary genericDictionary => genericDictionary[map.Key.ToString()], + IDictionary dictionary => dictionary[map.Key], + _ => map.MemberAccessor.GetValue(currentValue) }; - result.Add(info); + result.Add(new CellWriteInfo(cellValue, column, map)); } return result.ToArray(); @@ -84,4 +81,4 @@ public async ValueTask DisposeAsync() _disposed = true; } } -} \ No newline at end of file +} diff --git a/src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs b/src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs index a4bb8815..952e246c 100644 --- a/src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs +++ b/src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs @@ -13,44 +13,48 @@ public bool TryGetKnownCount(out int count) public List GetColumns() { - var props = new List(); + var mappings = new List(); for (var i = 0; i < _reader.FieldCount; i++) { var columnName = _reader.GetName(i); if (!_configuration.DynamicColumnFirst || _configuration.DynamicColumns.Any(d => string.Equals(d.Key, columnName, StringComparison.OrdinalIgnoreCase))) { - var prop = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration); - props.Add(prop); + var map = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration); + mappings.Add(map); } } - return props; + return mappings; } - public IEnumerable> GetRows(List props, CancellationToken cancellationToken = default) + public IEnumerable GetRows(List mappings, CancellationToken cancellationToken = default) { while (_reader.Read()) { cancellationToken.ThrowIfCancellationRequested(); - yield return GetRowValues(props); + yield return GetRowValues(mappings); } } - private IEnumerable GetRowValues(List props) + private CellWriteInfo[] GetRowValues(List mappings) { var column = 1; + var result = new List(mappings.Count); + for (int i = 0; i < _reader.FieldCount; i++) { - var prop = props[i]; - if (prop is { ExcelIgnoreColumn: false }) + var map = mappings[i]; + if (map is not { ExcelIgnoreColumn: true }) { var columnIndex = _configuration.DynamicColumnFirst - ? _reader.GetOrdinal(prop.Key.ToString()) + ? _reader.GetOrdinal(map.Key.ToString()) : i; - - yield return new CellWriteInfo(_reader.GetValue(columnIndex), column, prop); + + result.Add(new CellWriteInfo(_reader.GetValue(columnIndex), column, map)); column++; } } + + return result.ToArray(); } -} \ No newline at end of file +} diff --git a/src/MiniExcel.Core/WriteAdapters/DataTableWriteAdapter.cs b/src/MiniExcel.Core/WriteAdapters/DataTableWriteAdapter.cs index 273d36bd..be82aa9a 100644 --- a/src/MiniExcel.Core/WriteAdapters/DataTableWriteAdapter.cs +++ b/src/MiniExcel.Core/WriteAdapters/DataTableWriteAdapter.cs @@ -13,30 +13,32 @@ public bool TryGetKnownCount(out int count) public List GetColumns() { - var props = new List(); + var mappings = new List(); for (var i = 0; i < _dataTable.Columns.Count; i++) { var columnName = _dataTable.Columns[i].Caption ?? _dataTable.Columns[i].ColumnName; - var prop = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration); - props.Add(prop); + var map = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration); + mappings.Add(map); } - return props; + return mappings; } - public IEnumerable> GetRows(List props, CancellationToken cancellationToken = default) + public IEnumerable GetRows(List mappings, CancellationToken cancellationToken = default) { for (int row = 0; row < _dataTable.Rows.Count; row++) { cancellationToken.ThrowIfCancellationRequested(); - yield return GetRowValues(row, props); + yield return GetRowValues(row, mappings); } } - private IEnumerable GetRowValues(int row, List props) + private CellWriteInfo[] GetRowValues(int row, List mappings) { + var result = new List(mappings.Count); for (int i = 0, column = 1; i < _dataTable.Columns.Count; i++, column++) { - yield return new CellWriteInfo(_dataTable.Rows[row][i], column, props[i]); + result.Add(new CellWriteInfo(_dataTable.Rows[row][i], column, mappings[i])); } + return result.ToArray(); } -} \ No newline at end of file +} diff --git a/src/MiniExcel.Core/WriteAdapters/EnumerableWriteAdapter.cs b/src/MiniExcel.Core/WriteAdapters/EnumerableWriteAdapter.cs index 19c12270..d2a9633b 100644 --- a/src/MiniExcel.Core/WriteAdapters/EnumerableWriteAdapter.cs +++ b/src/MiniExcel.Core/WriteAdapters/EnumerableWriteAdapter.cs @@ -23,8 +23,8 @@ public bool TryGetKnownCount(out int count) public List? GetColumns() { - if (ColumnMappingsProvider.TryGetColumnMappings(_genericType, _configuration, out var props)) - return props; + if (ColumnMappingsProvider.TryGetColumnMappings(_genericType, _configuration, out var mappings)) + return mappings; _enumerator = _values.GetEnumerator(); if (_enumerator.MoveNext()) @@ -42,7 +42,7 @@ public bool TryGetKnownCount(out int count) } } - public IEnumerable> GetRows(List props, CancellationToken cancellationToken = default) + public IEnumerable GetRows(List mappings, CancellationToken cancellationToken = default) { if (_empty) yield break; @@ -59,7 +59,7 @@ public IEnumerable> GetRows(List> GetRows(List GetRowValues(object currentValue, List props) + + private static CellWriteInfo[] GetRowValues(object currentValue, List mappings) { - var column = 1; - foreach (var prop in props) + var column = 0; + var result = new List(mappings.Count); + + foreach (var map in mappings) { - object? cellValue; - if (prop is null) - { - cellValue = null; - } - else if (currentValue is IDictionary genericDictionary) - { - cellValue = genericDictionary[prop.Key.ToString()]; - } - else if (currentValue is IDictionary dictionary) - { - cellValue = dictionary[prop.Key]; - } - else - { - cellValue = prop.MemberAccessor.GetValue(currentValue); - } - - yield return new CellWriteInfo(cellValue, column, prop); column++; + var cellValue = currentValue switch + { + _ when map is null => null, + IDictionary genericDictionary => genericDictionary[map.Key.ToString()], + IDictionary dictionary => dictionary[map.Key], + _ => map.MemberAccessor.GetValue(currentValue) + }; + result.Add(new CellWriteInfo(cellValue, column, map)); } + + return result.ToArray(); } -} \ No newline at end of file +} diff --git a/src/MiniExcel.Csv/CsvWriter.cs b/src/MiniExcel.Csv/CsvWriter.cs index 37c11ddf..cc24173e 100644 --- a/src/MiniExcel.Csv/CsvWriter.cs +++ b/src/MiniExcel.Csv/CsvWriter.cs @@ -24,7 +24,7 @@ internal CsvWriter(Stream stream, object? value, bool printHeader, IMiniExcelCon private void AppendColumn(StringBuilder rowBuilder, CellWriteInfo column) { - rowBuilder.Append(CsvSanitizer.SanitizeCsvField(ToCsvString(column.Value, column.Prop), _configuration)); + rowBuilder.Append(CsvSanitizer.SanitizeCsvField(ToCsvString(column.Value, column.Mapping), _configuration)); rowBuilder.Append(_configuration.Seperator); } @@ -51,14 +51,14 @@ private async Task WriteValuesAsync(StreamWriter writer, object values, str try { #if SYNC_ONLY - var props = writeAdapter?.GetColumns(); + var mappings = writeAdapter?.GetColumns(); #else - var props = writeAdapter is not null + var mappings = writeAdapter is not null ? writeAdapter.GetColumns() : await asyncWriteAdapter!.GetColumnsAsync().ConfigureAwait(false); #endif - if (props is null) + if (mappings is null) { await _writer.WriteAsync(_configuration.NewLine #if NET5_0_OR_GREATER @@ -75,7 +75,7 @@ await _writer.FlushAsync( if (_printHeader) { - await _writer.WriteAsync(GetHeader(props) + await _writer.WriteAsync(GetHeader(mappings) #if NET5_0_OR_GREATER .AsMemory(), cancellationToken #endif @@ -92,7 +92,7 @@ await _writer.WriteAsync(newLine if (writeAdapter is not null) { - foreach (var row in writeAdapter.GetRows(props, cancellationToken)) + foreach (var row in writeAdapter.GetRows(mappings, cancellationToken)) { rowBuilder.Clear(); foreach (var column in row) @@ -120,7 +120,7 @@ await _writer.WriteAsync(newLine else { #if !SYNC_ONLY - await foreach (var row in asyncWriteAdapter!.GetRowsAsync(props, cancellationToken).ConfigureAwait(false)) + await foreach (var row in asyncWriteAdapter!.GetRowsAsync(mappings, cancellationToken).ConfigureAwait(false)) { cancellationToken.ThrowIfCancellationRequested(); rowBuilder.Clear(); @@ -221,9 +221,9 @@ public string ToCsvString(object? value, MiniExcelColumnMapping? p) return Convert.ToString(value, _configuration.Culture) ?? ""; } - private string GetHeader(List props) => string.Join( + private string GetHeader(List mappings) => string.Join( _configuration.Seperator.ToString(), - props.Select(s => CsvSanitizer.SanitizeCsvField(s?.ExcelColumnName, _configuration))); + mappings.Select(s => CsvSanitizer.SanitizeCsvField(s?.ExcelColumnName, _configuration))); private void Dispose(bool disposing) { diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs b/src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs index 5859d926..0596742e 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs +++ b/src/MiniExcel.OpenXml/FluentMapping/MappingCellStreamAdapter.cs @@ -18,11 +18,10 @@ public bool TryGetKnownCount(out int count) public List GetColumns() { - var props = new List(); - + var mappings = new List(); for (int i = 0; i < _columnLetters.Length; i++) { - props.Add(new MiniExcelColumnMapping + mappings.Add(new MiniExcelColumnMapping { Key = _columnLetters[i], ExcelColumnName = _columnLetters[i], @@ -30,10 +29,10 @@ public List GetColumns() }); } - return props; + return mappings; } - public IEnumerable> GetRows(List props, CancellationToken cancellationToken = default) + public IEnumerable GetRows(List mappings, CancellationToken cancellationToken = default) { var currentRow = new Dictionary(); var currentRowIndex = 0; @@ -48,7 +47,7 @@ public IEnumerable> GetRows(List 0 && currentRow.Count > 0) { - yield return ConvertRowToCellWriteInfos(currentRow, props); + yield return ConvertRowToCellWriteInfos(currentRow, mappings); } // Start new row @@ -63,23 +62,22 @@ public IEnumerable> GetRows(List 0) { - yield return ConvertRowToCellWriteInfos(currentRow, props); + yield return ConvertRowToCellWriteInfos(currentRow, mappings); } } - private static IEnumerable ConvertRowToCellWriteInfos(Dictionary row, List props) + private static CellWriteInfo[] ConvertRowToCellWriteInfos(Dictionary row, List mappings) { - var columnIndex = 1; - foreach (var prop in props) + var columnIndex = 0; + var result = new List(mappings.Count); + + foreach (var map in mappings) { - object? cellValue = null; - if (row.TryGetValue(prop.Key.ToString(), out var value)) - { - cellValue = value; - } - - yield return new CellWriteInfo(cellValue, columnIndex, prop); columnIndex++; + var cellValue = row.GetValueOrDefault(map.Key.ToString()); + result.Add(new CellWriteInfo(cellValue, columnIndex, map)); } + + return result.ToArray(); } -} \ No newline at end of file +} diff --git a/src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs b/src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs index 54d8d629..cf9121fb 100644 --- a/src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs +++ b/src/MiniExcel.OpenXml/FluentMapping/MappingRegistry.cs @@ -1,4 +1,3 @@ -using System.Reflection; using MiniExcelLib.OpenXml.FluentMapping.Configuration; namespace MiniExcelLib.OpenXml.FluentMapping; @@ -62,9 +61,7 @@ public bool HasMapping() { lock (_lock) { - return _compiledMappings.TryGetValue(type, out var mapping) - ? mapping - : null; + return _compiledMappings.GetValueOrDefault(type); } } diff --git a/src/MiniExcel.OpenXml/OpenXmlWriter.cs b/src/MiniExcel.OpenXml/OpenXmlWriter.cs index e104fa5b..9d9e47ad 100644 --- a/src/MiniExcel.OpenXml/OpenXmlWriter.cs +++ b/src/MiniExcel.OpenXml/OpenXmlWriter.cs @@ -247,21 +247,21 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va var isKnownCount = writeAdapter is not null && writeAdapter.TryGetKnownCount(out count); #if SYNC_ONLY - var props = writeAdapter?.GetColumns(); + var mappings = writeAdapter?.GetColumns(); #else - var props = writeAdapter is not null + var mappings = writeAdapter is not null ? writeAdapter.GetColumns() : await (asyncWriteAdapter?.GetColumnsAsync() ?? Task.FromResult?>(null)).ConfigureAwait(false); #endif - if (props is null) + if (mappings is null) { await WriteEmptySheetAsync(writer).ConfigureAwait(false); return 0; } int maxRowIndex; - var maxColumnIndex = props.Count(x => x is { ExcelIgnoreColumn: false }); + var maxColumnIndex = mappings.Count(x => x is { ExcelIgnoreColumn: false }); long dimensionPlaceholderPostition = 0; await writer.WriteAsync(WorksheetXml.StartWorksheetWithRelationship, cancellationToken).ConfigureAwait(false); @@ -270,7 +270,7 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va if (isKnownCount) { maxRowIndex = _printHeader ? count + 1 : count; - await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, props.Count)), cancellationToken).ConfigureAwait(false); + await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, mappings.Count)), cancellationToken).ConfigureAwait(false); } else if (_configuration.FastMode) { @@ -286,11 +286,11 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va if (_configuration.EnableAutoWidth) { columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, maxColumnIndex, cancellationToken).ConfigureAwait(false); - widths = ExcelColumnWidthCollection.GetFromMappings(props!, _configuration.MinWidth, _configuration.MaxWidth); + widths = ExcelColumnWidthCollection.GetFromMappings(mappings!, _configuration.MinWidth, _configuration.MaxWidth); } else { - var colWidths = ExcelColumnWidthCollection.GetFromMappings(props!); + var colWidths = ExcelColumnWidthCollection.GetFromMappings(mappings!); await WriteColumnsWidthsAsync(writer, colWidths.Columns, cancellationToken).ConfigureAwait(false); } @@ -299,13 +299,13 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va var currentRowIndex = 0; if (_printHeader) { - await PrintHeaderAsync(writer, props!, cancellationToken).ConfigureAwait(false); + await PrintHeaderAsync(writer, mappings!, cancellationToken).ConfigureAwait(false); currentRowIndex++; } if (writeAdapter is not null) { - foreach (var row in writeAdapter.GetRows(props, cancellationToken)) + foreach (var row in writeAdapter.GetRows(mappings, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex), cancellationToken).ConfigureAwait(false); @@ -313,7 +313,7 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va foreach (var cellValue in row) { cancellationToken.ThrowIfCancellationRequested(); - await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths, cancellationToken).ConfigureAwait(false); + await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Mapping, widths, cancellationToken).ConfigureAwait(false); progress?.Report(1); } await writer.WriteAsync(WorksheetXml.EndRow, cancellationToken).ConfigureAwait(false); @@ -322,14 +322,14 @@ private async Task WriteValuesAsync(MiniExcelStreamWriter writer, object va else { #if !SYNC_ONLY - await foreach (var row in asyncWriteAdapter!.GetRowsAsync(props, cancellationToken).ConfigureAwait(false)) + await foreach (var row in asyncWriteAdapter!.GetRowsAsync(mappings, cancellationToken).ConfigureAwait(false)) { cancellationToken.ThrowIfCancellationRequested(); await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex), cancellationToken).ConfigureAwait(false); foreach (var cellValue in row) { - await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths, cancellationToken).ConfigureAwait(false); + await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Mapping, widths, cancellationToken).ConfigureAwait(false); progress?.Report(1); } await writer.WriteAsync(WorksheetXml.EndRow, cancellationToken).ConfigureAwait(false); @@ -423,22 +423,22 @@ private static async Task WriteColumnsWidthsAsync(MiniExcelStreamWriter writer, } [CreateSyncVersion] - private async Task PrintHeaderAsync(MiniExcelStreamWriter writer, List props, CancellationToken cancellationToken = default) + private async Task PrintHeaderAsync(MiniExcelStreamWriter writer, List mappings, CancellationToken cancellationToken = default) { const int yIndex = 1; await writer.WriteAsync(WorksheetXml.StartRow(yIndex), cancellationToken).ConfigureAwait(false); var xIndex = 1; - foreach (var p in props) + foreach (var map in mappings) { //reason : https://github.com/mini-software/MiniExcel/issues/142 - if (p is not null) + if (map is not null) { - if (p.ExcelIgnoreColumn) + if (map.ExcelIgnoreColumn) continue; var r = CellReferenceConverter.GetCellFromCoordinates(xIndex, yIndex); - await WriteCellAsync(writer, r, columnName: p.ExcelColumnName).ConfigureAwait(false); + await WriteCellAsync(writer, r, columnName: map.ExcelColumnName).ConfigureAwait(false); } xIndex++; } @@ -447,19 +447,19 @@ private async Task PrintHeaderAsync(MiniExcelStreamWriter writer, List SpecialCellType.Header, var generateCellValuesContext = new GenerateCellValuesContext() { - currentHeader = currentHeader, - headerDiff = headerDiff, - iEnumerableIndex = iEnumerableIndex, - isFirst = isFirst, - newRowIndex = newRowIndex, - prevHeader = prevHeader, - rowIndexDiff = rowIndexDiff, + CurrentHeader = currentHeader, + HeaderDiff = headerDiff, + EnumerableIndex = iEnumerableIndex, + IsFirst = isFirst, + NewRowIndex = newRowIndex, + PrevHeader = prevHeader, + RowIndexDiff = rowIndexDiff, }; generateCellValuesContext = await GenerateCellValuesAsync(generateCellValuesContext, endPrefix, writer, rowXml, mergeRowCount, isHeaderRow, rowInfo, row, groupingRowDiff, innerXml, outerXmlOpen, row, cancellationToken).ConfigureAwait(false); - rowIndexDiff = generateCellValuesContext.rowIndexDiff; - headerDiff = generateCellValuesContext.headerDiff; - prevHeader = generateCellValuesContext.prevHeader; - newRowIndex = generateCellValuesContext.newRowIndex; - isFirst = generateCellValuesContext.isFirst; - iEnumerableIndex = generateCellValuesContext.iEnumerableIndex; - currentHeader = generateCellValuesContext.currentHeader; + rowIndexDiff = generateCellValuesContext.RowIndexDiff; + headerDiff = generateCellValuesContext.HeaderDiff; + prevHeader = generateCellValuesContext.PrevHeader; + newRowIndex = generateCellValuesContext.NewRowIndex; + isFirst = generateCellValuesContext.IsFirst; + iEnumerableIndex = generateCellValuesContext.EnumerableIndex; + currentHeader = generateCellValuesContext.CurrentHeader; enumrowend = newRowIndex - 1; @@ -525,13 +525,13 @@ private async Task GenerateCellValuesAsync( XmlElement rowElement, CancellationToken cancellationToken = default) { - var rowIndexDiff = generateCellValuesContext.rowIndexDiff; - var headerDiff = generateCellValuesContext.headerDiff; - var prevHeader = generateCellValuesContext.prevHeader; - var newRowIndex = generateCellValuesContext.newRowIndex; - var isFirst = generateCellValuesContext.isFirst; - var iEnumerableIndex = generateCellValuesContext.iEnumerableIndex; - var currentHeader = generateCellValuesContext.currentHeader; + var rowIndexDiff = generateCellValuesContext.RowIndexDiff; + var headerDiff = generateCellValuesContext.HeaderDiff; + var prevHeader = generateCellValuesContext.PrevHeader; + var newRowIndex = generateCellValuesContext.NewRowIndex; + var isFirst = generateCellValuesContext.IsFirst; + var iEnumerableIndex = generateCellValuesContext.EnumerableIndex; + var currentHeader = generateCellValuesContext.CurrentHeader; // https://github.com/mini-software/MiniExcel/issues/771 Saving by template introduces unintended value replication in each row #771 var notFirstRowElement = rowElement.Clone(); @@ -601,11 +601,11 @@ private async Task GenerateCellValuesAsync( } else { - var prop = rowInfo.PropsMap[newLines[0]]; - value = prop.PropertyInfoOrFieldInfo switch + var map = rowInfo.MembersMap[newLines[0]]; + value = map.PropertyInfoOrFieldInfo switch { - PropertyInfoOrFieldInfo.PropertyInfo => prop.PropertyInfo.GetValue(item), - PropertyInfoOrFieldInfo.FieldInfo => prop.FieldInfo.GetValue(item), + PropertyInfoOrFieldInfo.PropertyInfo => map.PropertyInfo.GetValue(item), + PropertyInfoOrFieldInfo.FieldInfo => map.FieldInfo.GetValue(item), _ => string.Empty }; } @@ -638,26 +638,23 @@ private async Task GenerateCellValuesAsync( else { var replacements = new Dictionary(); -#if NETCOREAPP3_0_OR_GREATER string MatchDelegate(Match x) => replacements.GetValueOrDefault(x.Groups[1].Value, ""); -#else - string MatchDelegate(Match x) => replacements.TryGetValue(x.Groups[1].Value, out var repl) ? repl : ""; -#endif - foreach (var prop in rowInfo.PropsMap) + + foreach (var map in rowInfo.MembersMap) { - var propInfo = prop.Value.PropertyInfo; - var name = isDictOrTable ? prop.Key : propInfo.Name; + var propInfo = map.Value.PropertyInfo; + var name = isDictOrTable ? map.Key : propInfo.Name; var key = $"{rowInfo.IEnumerablePropName}.{name}"; object? cellValue; if (rowInfo.IsDictionary) { - if (!dict!.TryGetValue(prop.Key, out cellValue)) + if (!dict!.TryGetValue(map.Key, out cellValue)) continue; } else if (rowInfo.IsDataTable) { - cellValue = dataRow![prop.Key]; + cellValue = dataRow![map.Key]; } else { @@ -668,7 +665,7 @@ private async Task GenerateCellValuesAsync( continue; var type = isDictOrTable - ? prop.Value.UnderlyingTypePropType + ? map.Value.UnderlyingMemberType : Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType; string? cellValueStr; @@ -813,13 +810,13 @@ await writer.WriteAsync(CleanXml(newRow.OuterXml, endPrefix) return new GenerateCellValuesContext { - currentHeader = currentHeader, - headerDiff = headerDiff, - iEnumerableIndex = iEnumerableIndex, - isFirst = isFirst, - newRowIndex = newRowIndex, - prevHeader = prevHeader, - rowIndexDiff = rowIndexDiff, + CurrentHeader = currentHeader, + HeaderDiff = headerDiff, + EnumerableIndex = iEnumerableIndex, + IsFirst = isFirst, + NewRowIndex = newRowIndex, + PrevHeader = prevHeader, + RowIndexDiff = rowIndexDiff, }; } @@ -1168,17 +1165,17 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap foreach (var formatText in matches) { xRowInfo.FormatText = formatText; - var propNames = formatText.Split('.'); - if (propNames[0].StartsWith("$")) //e.g:"$rowindex" it doesn't need to check cell value type + var mapNames = formatText.Split('.'); + if (mapNames[0].StartsWith("$")) //e.g:"$rowindex" it doesn't need to check cell value type continue; // TODO: default if not contain property key, clean the template string - if (!inputMaps.TryGetValue(propNames[0], out var cellValue)) + if (!inputMaps.TryGetValue(mapNames[0], out var cellValue)) { if (!_configuration.IgnoreTemplateParameterMissing) - throw new KeyNotFoundException($"The parameter '{propNames[0]}' was not found."); + throw new KeyNotFoundException($"The parameter '{mapNames[0]}' was not found."); - v.InnerText = v.InnerText.Replace($"{{{{{propNames[0]}}}}}", ""); + v.InnerText = v.InnerText.Replace($"{{{{{mapNames[0]}}}}}", ""); break; } @@ -1203,17 +1200,17 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap xRowInfo.CellIEnumerableValuesCount++; if (xRowInfo.IEnumerableGenericType is null && element is not null) { - xRowInfo.IEnumerablePropName = propNames[0]; + xRowInfo.IEnumerablePropName = mapNames[0]; xRowInfo.IEnumerableGenericType = element.GetType(); if (element is IDictionary dic) { xRowInfo.IsDictionary = true; - xRowInfo.PropsMap = dic.ToDictionary( + xRowInfo.MembersMap = dic.ToDictionary( kv => kv.Key, kv => kv.Value is not null - ? new MemberInfo { UnderlyingTypePropType = Nullable.GetUnderlyingType(kv.Value.GetType()) ?? kv.Value.GetType() } - : new MemberInfo { UnderlyingTypePropType = typeof(object) }); + ? new MemberInfo { UnderlyingMemberType = Nullable.GetUnderlyingType(kv.Value.GetType()) ?? kv.Value.GetType() } + : new MemberInfo { UnderlyingMemberType = typeof(object) }); } else { @@ -1224,7 +1221,7 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap { PropertyInfo = p, PropertyInfoOrFieldInfo = PropertyInfoOrFieldInfo.PropertyInfo, - UnderlyingTypePropType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType + UnderlyingMemberType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType }); var fields = xRowInfo.IEnumerableGenericType.GetFields(); @@ -1232,17 +1229,17 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap { if (!values.ContainsKey(f.Name)) { - var propInfo = new MemberInfo + var fieldInfo = new MemberInfo { FieldInfo = f, PropertyInfoOrFieldInfo = PropertyInfoOrFieldInfo.FieldInfo, - UnderlyingTypePropType = Nullable.GetUnderlyingType(f.FieldType) ?? f.FieldType + UnderlyingMemberType = Nullable.GetUnderlyingType(f.FieldType) ?? f.FieldType }; - values.Add(f.Name, propInfo); + values.Add(f.Name, fieldInfo); } } - xRowInfo.PropsMap = values; + xRowInfo.MembersMap = values; } } @@ -1257,21 +1254,21 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap //only check first one match IEnumerable, so only render one collection at same row // Empty collection parameter will get exception https://gitee.com/dotnetchina/MiniExcel/issues/I4WM67 - if (xRowInfo.PropsMap is null) + if (xRowInfo.MembersMap is null) { - v.InnerText = v.InnerText.Replace($"{{{{{propNames[0]}}}}}", propNames[1]); + v.InnerText = v.InnerText.Replace($"{{{{{mapNames[0]}}}}}", mapNames[1]); break; } - if (!xRowInfo.PropsMap.TryGetValue(propNames[1], out var prop)) + if (!xRowInfo.MembersMap.TryGetValue(mapNames[1], out var map)) { - v.InnerText = v.InnerText.Replace($"{{{{{propNames[0]}.{propNames[1]}}}}}", ""); + v.InnerText = v.InnerText.Replace($"{{{{{mapNames[0]}.{mapNames[1]}}}}}", ""); continue; //why unreachable exception? - throw new InvalidDataException($"{propNames[0]} doesn't have {propNames[1]} property"); + throw new InvalidDataException($"{mapNames[0]} doesn't have {mapNames[1]} property"); } // auto check type https://github.com/mini-software/MiniExcel/issues/177 - var type = prop.UnderlyingTypePropType; //avoid nullable + var type = map.UnderlyingMemberType; //avoid nullable if (isMultiMatch) { @@ -1296,7 +1293,7 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap { if (xRowInfo.CellIEnumerableValues is null) { - xRowInfo.IEnumerablePropName = propNames[0]; + xRowInfo.IEnumerablePropName = mapNames[0]; xRowInfo.IEnumerableGenericType = typeof(DataRow); xRowInfo.IsDataTable = true; @@ -1314,16 +1311,16 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap } //TODO:need to optimize //maxRowIndexDiff = dt.Rows.Count <= 1 ? 0 : dt.Rows.Count-1; - xRowInfo.PropsMap = dt.Columns.Cast().ToDictionary(col => + xRowInfo.MembersMap = dt.Columns.Cast().ToDictionary(col => col.ColumnName, - col => new MemberInfo { UnderlyingTypePropType = Nullable.GetUnderlyingType(col.DataType) } + col => new MemberInfo { UnderlyingMemberType = Nullable.GetUnderlyingType(col.DataType) } ); } - var column = dt.Columns[propNames[1]]; + var column = dt.Columns[mapNames[1]]; var type = Nullable.GetUnderlyingType(column.DataType) ?? column.DataType; //avoid nullable - if (!xRowInfo.PropsMap.ContainsKey(propNames[1])) - throw new InvalidDataException($"{propNames[0]} doesn't have {propNames[1]} property"); + if (!xRowInfo.MembersMap.ContainsKey(mapNames[1])) + throw new InvalidDataException($"{mapNames[0]} doesn't have {mapNames[1]} property"); if (isMultiMatch) { @@ -1372,7 +1369,7 @@ private void UpdateDimensionAndGetRowsInfo(IDictionary inputMap // Re-acquire v after SetCellType may have changed DOM structure v = c.SelectSingleNode("x:v", Ns) ?? c.SelectSingleNode("x:is/x:t", Ns); - v.InnerText = v.InnerText.Replace($"{{{{{propNames[0]}}}}}", cellValueStr); //TODO: auto check type and set value + v.InnerText = v.InnerText.Replace($"{{{{{mapNames[0]}}}}}", cellValueStr); //TODO: auto check type and set value } } //if (xRowInfo.CellIEnumerableValues is not null) //2. From left to right, only the first set is used as the basis for the list diff --git a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplateUtils.cs b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplateUtils.cs index 473fb171..608269af 100644 --- a/src/MiniExcel.OpenXml/Templates/OpenXmlTemplateUtils.cs +++ b/src/MiniExcel.OpenXml/Templates/OpenXmlTemplateUtils.cs @@ -1,6 +1,4 @@ -using System.Xml.Linq; - -namespace MiniExcelLib.OpenXml.Templates; +namespace MiniExcelLib.OpenXml.Templates; internal class XRowInfo { @@ -8,7 +6,7 @@ internal class XRowInfo public string IEnumerablePropName { get; set; } public XmlElement Row { get; set; } public Type IEnumerableGenericType { get; set; } - public IDictionary PropsMap { get; set; } + public IDictionary MembersMap { get; set; } public bool IsDictionary { get; set; } public bool IsDataTable { get; set; } public int CellIEnumerableValuesCount { get; set; } @@ -17,15 +15,13 @@ internal class XRowInfo public XMergeCell? IEnumerableMercell { get; set; } public List? RowMercells { get; set; } public List? ConditionalFormats { get; set; } - - } internal class MemberInfo { public PropertyInfo PropertyInfo { get; set; } public FieldInfo FieldInfo { get; set; } - public Type UnderlyingTypePropType { get; set; } + public Type UnderlyingMemberType { get; set; } public PropertyInfoOrFieldInfo PropertyInfoOrFieldInfo { get; set; } = PropertyInfoOrFieldInfo.None; } @@ -48,6 +44,7 @@ public XMergeCell(XMergeCell mergeCell) Y2 = mergeCell.Y2; MergeCell = mergeCell.MergeCell; } + public XMergeCell(XmlElement mergeCell) { var refAttr = mergeCell.Attributes["ref"].Value; @@ -64,6 +61,7 @@ public XMergeCell(XmlElement mergeCell) Width = Math.Abs(X1 - X2) + 1; Height = Math.Abs(Y1 - Y2) + 1; } + public XMergeCell(string x1, int y1, string x2, int y2) { X1 = CellReferenceConverter.GetNumericalIndex(x1); @@ -124,11 +122,11 @@ internal enum SpecialCellType { None, Group, Endgroup, Merge, Header } internal class GenerateCellValuesContext { - public int rowIndexDiff { get; set; } - public int headerDiff { get; set; } - public string prevHeader { get; set; } - public string currentHeader { get; set; } - public int newRowIndex { get; set; } - public bool isFirst { get; set; } - public int iEnumerableIndex { get; set; } + public int RowIndexDiff { get; set; } + public int HeaderDiff { get; set; } + public string PrevHeader { get; set; } + public string CurrentHeader { get; set; } + public int NewRowIndex { get; set; } + public bool IsFirst { get; set; } + public int EnumerableIndex { get; set; } } diff --git a/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs b/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs index 414fd835..0d3c845b 100644 --- a/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs +++ b/src/MiniExcel.OpenXml/Zip/OpenXmlZip.cs @@ -43,10 +43,7 @@ public OpenXmlZip(Stream fileStream, ZipArchiveMode mode = ZipArchiveMode.Read, } } - public ZipArchiveEntry? GetEntry(string path) - { - return Entries.TryGetValue(path, out var entry) ? entry : null; - } + public ZipArchiveEntry? GetEntry(string path) => Entries.GetValueOrDefault(path); public XmlReader? GetXmlReader(string path) { @@ -82,4 +79,4 @@ protected virtual void Dispose(bool disposing) _disposed = true; } } -} \ No newline at end of file +}