Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MiniExcel.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<File Path="docs\README.zh-Hant.md" />
</Folder>
<Folder Name="/src/">
<File Path="src/.editorconfig" />
<File Path="src\Directory.Build.props" />
<File Path="src\Directory.Packages.props" />
<Project Path="src\MiniExcel.OpenXml/MiniExcel.OpenXml.csproj" />
Expand Down
1 change: 1 addition & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/MiniExcel.Core/Abstractions/IMiniExcelWriteAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ public interface IMiniExcelWriteAdapter
{
bool TryGetKnownCount(out int count);
List<MiniExcelColumnMapping>? GetColumns();
IEnumerable<IEnumerable<CellWriteInfo>> GetRows(List<MiniExcelColumnMapping> props, CancellationToken cancellationToken = default);
IEnumerable<CellWriteInfo[]> GetRows(List<MiniExcelColumnMapping> 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;
}
public MiniExcelColumnMapping? Mapping { get; } = mapping;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
public interface IMiniExcelWriteAdapterAsync
{
Task<List<MiniExcelColumnMapping>?> GetColumnsAsync();
IAsyncEnumerable<CellWriteInfo[]> GetRowsAsync(List<MiniExcelColumnMapping> props, CancellationToken cancellationToken);
IAsyncEnumerable<CellWriteInfo[]> GetRowsAsync(List<MiniExcelColumnMapping> mappings, CancellationToken cancellationToken);
}
11 changes: 11 additions & 0 deletions src/MiniExcel.Core/Helpers/NetSatandardExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace MiniExcelLib.Core.Helpers;

public static class NetStandardExtensions
{
#if NETSTANDARD2_0
public static TValue? GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue? defaultValue = default)
{
return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
}
#endif
}
60 changes: 30 additions & 30 deletions src/MiniExcel.Core/Reflection/ColumnMappingsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ internal static List<MiniExcelColumnMapping> GetMappingsForImport(Type type, str

private static List<MiniExcelColumnMapping?> 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<MiniExcelColumnMapping?> SortMappings(List<MiniExcelColumnMapping?> mappings)
Expand All @@ -81,29 +81,29 @@ internal static List<MiniExcelColumnMapping> 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<MiniExcelColumnMapping?>();
var newMappings = new List<MiniExcelColumnMapping?>();
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<MiniExcelColumnMapping?> GetColumnMappings(Type type, BindingFlags bindingFlags, MiniExcelBaseConfiguration configuration)
Expand Down Expand Up @@ -156,23 +156,23 @@ internal static List<MiniExcelColumnMapping> GetMappingsForImport(Type type, str

private static List<MiniExcelColumnMapping?> GetDictionaryColumnInfo(IDictionary<string, object?>? dicString, IDictionary? dic, MiniExcelBaseConfiguration configuration)
{
var props = new List<MiniExcelColumnMapping?>();
var mappings = new List<MiniExcelColumnMapping?>();

var keys = dicString?.Keys.ToList()
?? dic?.Keys
?? throw new InvalidOperationException();

foreach (var key in keys)
{
SetDictionaryColumnInfo(props, key, configuration);
SetDictionaryColumnInfo(mappings, key, configuration);
}

return SortMappings(props);
return SortMappings(mappings);
}

private static void SetDictionaryColumnInfo(List<MiniExcelColumnMapping?> props, object key, MiniExcelBaseConfiguration configuration)
private static void SetDictionaryColumnInfo(List<MiniExcelColumnMapping?> mappings, object key, MiniExcelBaseConfiguration configuration)
{
var mapping = new MiniExcelColumnMapping
var map = new MiniExcelColumnMapping
{
Key = key,
ExcelColumnName = key?.ToString()
Expand All @@ -185,40 +185,40 @@ private static void SetDictionaryColumnInfo(List<MiniExcelColumnMapping?> 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<MiniExcelColumnMapping?> props)
internal static bool TryGetColumnMappings(Type? type, MiniExcelBaseConfiguration configuration, out List<MiniExcelColumnMapping?> mappings)
{
props = [];
mappings = [];

// Unknown type
if (type is null)
Expand All @@ -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;
}

Expand Down
27 changes: 12 additions & 15 deletions src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{
if (ColumnMappingsProvider.TryGetColumnMappings(typeof(T), _configuration, out var mappings))
{
return mappings;

Check warning on line 17 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Nullability of reference types in value of type 'List<MiniExcelColumnMapping?>' doesn't match target type 'List<MiniExcelColumnMapping>'.

Check warning on line 17 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in value of type 'List<MiniExcelColumnMapping?>' doesn't match target type 'List<MiniExcelColumnMapping>'.
}

_enumerator = _values.GetAsyncEnumerator();
Expand All @@ -24,10 +24,10 @@
return null;
}

return ColumnMappingsProvider.GetColumnMappingFromValue(_enumerator.Current, _configuration);

Check warning on line 27 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Possible null reference argument for parameter 'value' in 'List<MiniExcelColumnMapping?> ColumnMappingsProvider.GetColumnMappingFromValue(object value, MiniExcelBaseConfiguration configuration)'.

Check warning on line 27 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in value of type 'List<MiniExcelColumnMapping?>' doesn't match target type 'List<MiniExcelColumnMapping>'.

Check warning on line 27 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'value' in 'List<MiniExcelColumnMapping?> ColumnMappingsProvider.GetColumnMappingFromValue(object value, MiniExcelBaseConfiguration configuration)'.
}

public async IAsyncEnumerable<CellWriteInfo[]> GetRowsAsync(List<MiniExcelColumnMapping> props, [EnumeratorCancellation] CancellationToken cancellationToken)
public async IAsyncEnumerable<CellWriteInfo[]> GetRowsAsync(List<MiniExcelColumnMapping> mappings, [EnumeratorCancellation] CancellationToken cancellationToken)
{
if (_empty)
yield break;
Expand All @@ -44,30 +44,27 @@
do
{
cancellationToken.ThrowIfCancellationRequested();
yield return GetRowValues(_enumerator.Current, props);
yield return GetRowValues(_enumerator.Current, mappings);

Check warning on line 47 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / build

Argument of type 'List<MiniExcelColumnMapping>' cannot be used for parameter 'mappings' of type 'List<MiniExcelColumnMapping?>' in 'CellWriteInfo[] AsyncEnumerableWriteAdapter<T>.GetRowValues(T currentValue, List<MiniExcelColumnMapping?> mappings)' due to differences in the nullability of reference types.
}
while (await _enumerator.MoveNextAsync().ConfigureAwait(false));
}

private static CellWriteInfo[] GetRowValues(T currentValue, List<MiniExcelColumnMapping> props)
private static CellWriteInfo[] GetRowValues(T currentValue, List<MiniExcelColumnMapping?> mappings)
{
var column = 0;
var result = new List<CellWriteInfo>();
var result = new List<CellWriteInfo>(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<string, object> 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<string, object> genericDictionary => genericDictionary[map.Key.ToString()],
IDictionary dictionary => dictionary[map.Key],
_ => map.MemberAccessor.GetValue(currentValue)

Check warning on line 65 in src/MiniExcel.Core/WriteAdapters/AsyncEnumerableWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'instance' in 'object? MiniExcelMemberAccessor.GetValue(object instance)'.
};
result.Add(info);
result.Add(new CellWriteInfo(cellValue, column, map));
}

return result.ToArray();
Expand All @@ -84,4 +81,4 @@
_disposed = true;
}
}
}
}
30 changes: 17 additions & 13 deletions src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,48 @@

public List<MiniExcelColumnMapping> GetColumns()
{
var props = new List<MiniExcelColumnMapping>();
var mappings = new List<MiniExcelColumnMapping>();
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)))

Check warning on line 21 in src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Possible null reference argument for parameter 'source' in 'bool Enumerable.Any<DynamicExcelColumn>(IEnumerable<DynamicExcelColumn> source, Func<DynamicExcelColumn, bool> predicate)'.
{
var prop = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration);
props.Add(prop);
var map = ColumnMappingsProvider.GetColumnMappingFromDynamicConfiguration(columnName, _configuration);
mappings.Add(map);
}
}
return props;
return mappings;
}

public IEnumerable<IEnumerable<CellWriteInfo>> GetRows(List<MiniExcelColumnMapping> props, CancellationToken cancellationToken = default)
public IEnumerable<CellWriteInfo[]> GetRows(List<MiniExcelColumnMapping> mappings, CancellationToken cancellationToken = default)
{
while (_reader.Read())
{
cancellationToken.ThrowIfCancellationRequested();
yield return GetRowValues(props);
yield return GetRowValues(mappings);
}
}

private IEnumerable<CellWriteInfo> GetRowValues(List<MiniExcelColumnMapping> props)
private CellWriteInfo[] GetRowValues(List<MiniExcelColumnMapping> mappings)
{
var column = 1;
var result = new List<CellWriteInfo>(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())

Check warning on line 50 in src/MiniExcel.Core/WriteAdapters/DataReaderWriteAdapter.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Possible null reference argument for parameter 'name' in 'int IDataRecord.GetOrdinal(string name)'.
: i;
yield return new CellWriteInfo(_reader.GetValue(columnIndex), column, prop);

result.Add(new CellWriteInfo(_reader.GetValue(columnIndex), column, map));
column++;
}
}

return result.ToArray();
}
}
}
20 changes: 11 additions & 9 deletions src/MiniExcel.Core/WriteAdapters/DataTableWriteAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,32 @@ public bool TryGetKnownCount(out int count)

public List<MiniExcelColumnMapping> GetColumns()
{
var props = new List<MiniExcelColumnMapping>();
var mappings = new List<MiniExcelColumnMapping>();
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<IEnumerable<CellWriteInfo>> GetRows(List<MiniExcelColumnMapping> props, CancellationToken cancellationToken = default)
public IEnumerable<CellWriteInfo[]> GetRows(List<MiniExcelColumnMapping> 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<CellWriteInfo> GetRowValues(int row, List<MiniExcelColumnMapping> props)
private CellWriteInfo[] GetRowValues(int row, List<MiniExcelColumnMapping> mappings)
{
var result = new List<CellWriteInfo>(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();
}
}
}
Loading
Loading