新增按开始单元格和结束单元格范围读取

This commit is contained in:
离歌笑 2022-09-27 11:23:50 +08:00
parent 87d6eb65b3
commit 9257aa901a
4 changed files with 581 additions and 28 deletions

View File

@ -91,5 +91,68 @@ namespace MiniExcelLibs.Csv
public void Dispose()
{
}
//2022-09-24 excelReaderRange
#region Range
public IEnumerable<IDictionary<string, object>> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell)
{
if (startCell != "A1")
throw new NotImplementedException("CSV not Implement startCell");
if (_stream.CanSeek)
_stream.Position = 0;
var reader = _config.StreamReaderFunc(_stream);
{
var row = string.Empty;
string[] read;
var firstRow = true;
Dictionary<int, string> headRows = new Dictionary<int, string>();
while ((row = reader.ReadLine()) != null)
{
read = Split(row);
//header
if (useHeaderRow)
{
if (firstRow)
{
firstRow = false;
for (int i = 0; i <= read.Length - 1; i++)
headRows.Add(i, read[i]);
continue;
}
var cell = CustomPropertyHelper.GetEmptyExpandoObject(headRows);
for (int i = 0; i <= read.Length - 1; i++)
cell[headRows[i]] = read[i];
yield return cell;
continue;
}
//body
{
var cell = CustomPropertyHelper.GetEmptyExpandoObject(read.Length - 1, 0);
for (int i = 0; i <= read.Length - 1; i++)
cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i];
yield return cell;
}
}
}
}
public IEnumerable<T> QueryRange<T>(string sheetName, string startCell, string endCel) where T : class, new()
{
return ExcelOpenXmlSheetReader.QueryImplRange<T>(QueryRange(false, sheetName, startCell, endCel), startCell, endCel, this._config);
}
public Task<IEnumerable<IDictionary<string, object>>> QueryAsyncRange(bool UseHeaderRow, string sheetName, string startCell, string endCel, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.Run(() => QueryRange(UseHeaderRow, sheetName, startCell, endCel), cancellationToken);
}
public Task<IEnumerable<T>> QueryAsyncRange<T>(string sheetName, string startCell, string endCel, CancellationToken cancellationToken = default(CancellationToken)) where T : class, new()
{
return Task.Run(() => Query<T>(sheetName, startCell), cancellationToken);
}
#endregion
}
}

View File

@ -15,5 +15,15 @@ namespace MiniExcelLibs
IEnumerable<T> Query<T>(string sheetName, string startCell) where T : class, new();
Task<IEnumerable<IDictionary<string, object>>> QueryAsync(bool UseHeaderRow, string sheetName, string startCell,CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<T>> QueryAsync<T>(string sheetName, string startCell,CancellationToken cancellationToken = default(CancellationToken)) where T : class, new();
//
IEnumerable<IDictionary<string, object>> QueryRange(bool UseHeaderRow, string sheetName, string startCell, string endCell);
IEnumerable<T> QueryRange<T>(string sheetName, string startCell, string endCell) where T : class, new();
Task<IEnumerable<IDictionary<string, object>>> QueryAsyncRange(bool UseHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default(CancellationToken));
Task<IEnumerable<T>> QueryAsyncRange<T>(string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default(CancellationToken)) where T : class, new();
//
}
}

View File

@ -47,7 +47,7 @@
ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).Insert();
}
public static void SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null,bool overwriteFile = false)
public static void SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool overwriteFile = false)
{
if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm")
throw new NotSupportedException("MiniExcel SaveAs not support xlsm");
@ -77,6 +77,7 @@
}
}
//1
public static IEnumerable<dynamic> Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
using (var stream = FileHelper.OpenSharedRead(path))
@ -84,6 +85,7 @@
yield return item;
}
//2
public static IEnumerable<dynamic> Query(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration))
@ -92,10 +94,42 @@
(dict, p) => { dict.Add(p); return dict; });
}
#region range
//3
/// <summary>
///
/// </summary>
/// <param name="path">路径</param>
/// <param name="useHeaderRow">表头</param>
/// <param name="sheetName">表名称</param>
/// <param name="excelType">excel类型</param>
/// <param name="startCell">开始单元格,支持为空读所有,默认A1或者B列或者B2单元格</param>
/// <param name="endCell">结束单元格支持为空读所有或者为D别或者D2单元格</param>
/// <param name="configuration">配置</param>
/// <returns></returns>
public static IEnumerable<dynamic> QueryRange(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "a1", string endCell = "", IConfiguration configuration = null)
{
using (var stream = FileHelper.OpenSharedRead(path))
foreach (var item in QueryRange(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell == "" ? "a1" : startCell, endCell, configuration))
yield return item;
}
//4
public static IEnumerable<dynamic> QueryRange(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "a1", string endCell = "", IConfiguration configuration = null)
{
using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration))
foreach (var item in excelReader.QueryRange(useHeaderRow, sheetName, startCell == "" ? "a1" : startCell, endCell))
yield return item.Aggregate(new ExpandoObject() as IDictionary<string, object>,
(dict, p) => { dict.Add(p); return dict; });
}
#endregion range
public static void SaveAsByTemplate(string path, string templatePath, object value, IConfiguration configuration = null)
{
using (var stream = File.Create(path))
SaveAsByTemplate(stream, templatePath, value,configuration);
SaveAsByTemplate(stream, templatePath, value, configuration);
}
public static void SaveAsByTemplate(string path, byte[] templateBytes, object value, IConfiguration configuration = null)
@ -122,9 +156,10 @@
{
using (var stream = FileHelper.OpenSharedRead(path))
{
return QueryAsDataTable(stream, useHeaderRow, sheetName, excelType:ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration);
return QueryAsDataTable(stream, useHeaderRow, sheetName, excelType: ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration);
}
}
public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
if (sheetName == null && excelType != ExcelType.CSV) /*Issue #279*/
@ -132,7 +167,7 @@
var dt = new DataTable(sheetName);
var first = true;
var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType),configuration).Query(useHeaderRow, sheetName, startCell);
var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration).Query(useHeaderRow, sheetName, startCell);
var keys = new List<string>();
foreach (IDictionary<string, object> row in rows)
@ -175,7 +210,7 @@
public static List<string> GetSheetNames(this Stream stream)
{
var archive = new ExcelOpenXmlZip(stream);
return new ExcelOpenXmlSheetReader(stream,null).GetWorkbookRels(archive.entries).Select(s => s.Name).ToList();
return new ExcelOpenXmlSheetReader(stream, null).GetWorkbookRels(archive.entries).Select(s => s.Name).ToList();
}
public static ICollection<string> GetColumns(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)

View File

@ -16,6 +16,8 @@ namespace MiniExcelLibs.OpenXml
{
internal class ExcelOpenXmlSheetReader : IExcelReader
{
#region MyRegion
private bool _disposed = false;
private static readonly string[] _ns = { Config.SpreadsheetmlXmlns, Config.SpreadsheetmlXmlStrictns };
private static readonly string[] _relationshiopNs = { Config.SpreadsheetmlXmlRelationshipns, Config.SpreadsheetmlXmlStrictRelationshipns };
@ -25,6 +27,7 @@ namespace MiniExcelLibs.OpenXml
private ExcelOpenXmlStyles _style;
private readonly ExcelOpenXmlZip _archive;
private OpenXmlConfiguration _config;
private static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings
{
IgnoreComments = true,
@ -43,7 +46,8 @@ namespace MiniExcelLibs.OpenXml
{
if (!ReferenceHelper.ParseReference(startCell, out var startColumnIndex, out var startRowIndex))
throw new InvalidDataException($"startCell {startCell} is Invalid");
startColumnIndex--; startRowIndex--;
startColumnIndex--;
startRowIndex--;
// if sheets count > 1 need to read xl/_rels/workbook.xml.rels
var sheets = _archive.entries.Where(w => w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
@ -67,8 +71,8 @@ namespace MiniExcelLibs.OpenXml
else
sheetEntry = sheets.Single();
#region MergeCells
if (_config.FillMergedCells)
{
_mergeCells = new MergeCells();
@ -120,10 +124,8 @@ namespace MiniExcelLibs.OpenXml
}
}
}
#endregion
#endregion MergeCells
// TODO: need to optimize performance
var withoutCR = false;
@ -211,7 +213,6 @@ namespace MiniExcelLibs.OpenXml
maxColumnIndex = Math.Max(maxColumnIndex, cellIndex);
}
if (!XmlReaderHelper.SkipContent(reader))
break;
}
@ -228,13 +229,9 @@ namespace MiniExcelLibs.OpenXml
break;
}
}
}
}
using (var sheetStream = sheetEntry.Open())
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
{
@ -276,7 +273,6 @@ namespace MiniExcelLibs.OpenXml
continue;
}
// fill empty rows
if (!(nextRowIndex < startRowIndex))
{
@ -350,7 +346,6 @@ namespace MiniExcelLibs.OpenXml
continue;
}
yield return cell;
}
}
@ -359,7 +354,6 @@ namespace MiniExcelLibs.OpenXml
break;
}
}
}
else if (!XmlReaderHelper.SkipContent(reader))
{
@ -420,7 +414,6 @@ namespace MiniExcelLibs.OpenXml
var rowIndex = 0;
foreach (var item in values)
{
if (first)
{
keys = item.Keys.ToArray();//.Select((s, i) => new { s,i}).ToDictionary(_=>_.s,_=>_.i);
@ -437,7 +430,7 @@ namespace MiniExcelLibs.OpenXml
)
).ToDictionary(_ => _.key, _ => _.idx);
//TODO: alert don't duplicate column name
props = CustomPropertyHelper.GetExcelCustomPropertyInfos(type, keys,configuration);
props = CustomPropertyHelper.GetExcelCustomPropertyInfos(type, keys, configuration);
first = false;
continue;
}
@ -461,7 +454,6 @@ namespace MiniExcelLibs.OpenXml
}
}
//Q: Why need to check every time? A: it needs to check everytime, because it's dictionary
{
object newV = null;
@ -644,8 +636,6 @@ namespace MiniExcelLibs.OpenXml
}
}
return value;
}
@ -668,6 +658,7 @@ namespace MiniExcelLibs.OpenXml
}
value = null;
return;
case "inlineStr":
case "str":
//TODO: it will unbox,box
@ -675,7 +666,7 @@ namespace MiniExcelLibs.OpenXml
if (_config.EnableConvertByteArray)
{
//if str start with "data:image/png;base64," then convert to byte[] https://github.com/shps951023/MiniExcel/issues/318
if (v != null && v.StartsWith("@@@fileid@@@,",StringComparison.Ordinal))
if (v != null && v.StartsWith("@@@fileid@@@,", StringComparison.Ordinal))
{
var path = v.Substring(13);
var entry = _archive.GetEntry(path);
@ -697,9 +688,11 @@ namespace MiniExcelLibs.OpenXml
value = v;
}
return;
case "b":
value = rawValue == "1";
return;
case "d":
if (DateTime.TryParseExact(rawValue, "yyyy-MM-dd", invariantCulture, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite, out var date))
{
@ -709,9 +702,11 @@ namespace MiniExcelLibs.OpenXml
value = rawValue;
return;
case "e":
value = rawValue;
return;
default:
if (double.TryParse(rawValue, style, invariantCulture, out var n))
{
@ -761,5 +756,455 @@ namespace MiniExcelLibs.OpenXml
_disposed = true;
}
}
#endregion MyRegion
#region ReaderRange
public IEnumerable<IDictionary<string, object>> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell)
{
//2022-09-27
if (!ReferenceHelper.ParseReference(startCell, out var startColumnIndex, out var startRowIndex) == false ? true : true)
{
//throw new InvalidDataException($"startCell {startCell} is Invalid");
startColumnIndex--;
startRowIndex--;
if (startRowIndex < 0)
{
startRowIndex = 0;
}
if (startColumnIndex < 0)
{
startColumnIndex = 0;
}
}
//2022-09-24 获取结束单元格的,行,列
if (!ReferenceHelper.ParseReference(endCell, out var endColumnIndex, out var endRowIndex) == false ? true : true)
{
//throw new InvalidDataException($"endCell {endCell} is Invalid");
endColumnIndex--;
endRowIndex--;
}
// if sheets count > 1 need to read xl/_rels/workbook.xml.rels
var sheets = _archive.entries.Where(w => w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
|| w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
);
ZipArchiveEntry sheetEntry = null;
if (sheetName != null)
{
SetWorkbookRels(_archive.entries);
var s = _sheetRecords.SingleOrDefault(_ => _.Name == sheetName);
if (s == null)
throw new InvalidOperationException("Please check sheetName/Index is correct");
sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}" || w.FullName == s.Path || s.Path == $"/{w.FullName}");
}
else if (sheets.Count() > 1)
{
SetWorkbookRels(_archive.entries);
var s = _sheetRecords[0];
sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}");
}
else
sheetEntry = sheets.Single();
#region MergeCells
if (_config.FillMergedCells)
{
_mergeCells = new MergeCells();
using (var sheetStream = sheetEntry.Open())
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
{
if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns))
yield break;
while (reader.Read())
{
if (XmlReaderHelper.IsStartElement(reader, "mergeCells", _ns))
{
if (!XmlReaderHelper.ReadFirstContent(reader))
yield break;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "mergeCell", _ns))
{
var @ref = reader.GetAttribute("ref");
var refs = @ref.Split(':');
if (refs.Length == 1)
continue;
ReferenceHelper.ParseReference(refs[0], out var x1, out var y1);
ReferenceHelper.ParseReference(refs[1], out var x2, out var y2);
_mergeCells.MergesValues.Add(refs[0], null);
// foreach range
var isFirst = true;
for (int x = x1; x <= x2; x++)
{
for (int y = y1; y <= y2; y++)
{
if (!isFirst)
_mergeCells.MergesMap.Add(ReferenceHelper.ConvertXyToCell(x, y), refs[0]);
isFirst = false;
}
}
XmlReaderHelper.SkipContent(reader);
}
else if (!XmlReaderHelper.SkipContent(reader))
{
break;
}
}
}
}
}
}
#endregion MergeCells
// TODO: need to optimize performance
var withoutCR = false;
var maxRowIndex = -1;
var maxColumnIndex = -1;
//Q. why need 3 times openstream merge one open read? A. no, zipstream can't use position = 0
using (var sheetStream = sheetEntry.Open())
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
{
while (reader.Read())
{
if (XmlReaderHelper.IsStartElement(reader, "c", _ns))
{
var r = reader.GetAttribute("r");
if (r != null)
{
if (ReferenceHelper.ParseReference(r, out var column, out var row))
{
column--;
row--;
maxRowIndex = Math.Max(maxRowIndex, row);
maxColumnIndex = Math.Max(maxColumnIndex, column);
}
}
else
{
withoutCR = true;
break;
}
}
//this method logic depends on dimension to get maxcolumnIndex, if without dimension then it need to foreach all rows first time to get maxColumn and maxRowColumn
else if (XmlReaderHelper.IsStartElement(reader, "dimension", _ns))
{
//2022-09-24 Range
//var @ref = reader.GetAttribute("ref");
var @ref = startCell + ":" + endCell;
if (endCell == "" || startCell == "")
{
@ref = reader.GetAttribute("ref");
}
if (string.IsNullOrEmpty(@ref))
throw new InvalidOperationException("Without sheet dimension data");
var rs = @ref.Split(':');
// issue : https://github.com/shps951023/MiniExcel/issues/102
if (ReferenceHelper.ParseReference(rs.Length == 2 ? rs[1] : rs[0], out int cIndex, out int rIndex) == false ? true : true)
{
maxColumnIndex = cIndex - 1;
maxRowIndex = rIndex - 1;
break;
}
else
throw new InvalidOperationException("Invaild sheet dimension start data");
}
}
}
if (withoutCR)
{
using (var sheetStream = sheetEntry.Open())
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
{
if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns))
yield break;
if (!XmlReaderHelper.ReadFirstContent(reader))
yield break;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns))
{
if (!XmlReaderHelper.ReadFirstContent(reader))
continue;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "row", _ns))
{
maxRowIndex++;
if (!XmlReaderHelper.ReadFirstContent(reader))
continue;
//Cells
{
var cellIndex = -1;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "c", _ns))
{
cellIndex++;
maxColumnIndex = Math.Max(maxColumnIndex, cellIndex);
}
if (!XmlReaderHelper.SkipContent(reader))
break;
}
}
}
else if (!XmlReaderHelper.SkipContent(reader))
{
break;
}
}
}
else if (!XmlReaderHelper.SkipContent(reader))
{
break;
}
}
}
}
using (var sheetStream = sheetEntry.Open())
using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings))
{
if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns))
yield break;
if (!XmlReaderHelper.ReadFirstContent(reader))
yield break;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns))
{
if (!XmlReaderHelper.ReadFirstContent(reader))
continue;
Dictionary<int, string> headRows = new Dictionary<int, string>();
int rowIndex = -1;
int nextRowIndex = 0;
bool isFirstRow = true;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "row", _ns))
{
nextRowIndex = rowIndex + 1;
if (int.TryParse(reader.GetAttribute("r"), out int arValue))
rowIndex = arValue - 1; // The row attribute is 1-based
else
rowIndex++;
// row -> c
if (!XmlReaderHelper.ReadFirstContent(reader))
continue;
//2022-09-24跳过endcell结束单元格所在的行
if (rowIndex > endRowIndex && endRowIndex > 0)
{
break;
}
// 跳过startcell起始单元格所在的行
if (rowIndex < startRowIndex)
{
XmlReaderHelper.SkipToNextSameLevelDom(reader);
continue;
}
// fill empty rows
if (!(nextRowIndex < startRowIndex))
{
if (nextRowIndex < rowIndex)
{
for (int i = nextRowIndex; i < rowIndex; i++)
{
yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex);
}
}
}
// Set Cells
{
var cell = GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex);
var columnIndex = withoutCR ? -1 : 0;
while (!reader.EOF)
{
if (XmlReaderHelper.IsStartElement(reader, "c", _ns))
{
var aS = reader.GetAttribute("s");
var aR = reader.GetAttribute("r");
var aT = reader.GetAttribute("t");
var cellValue = ReadCellAndSetColumnIndex(reader, ref columnIndex, withoutCR, startColumnIndex, aR, aT);
if (_config.FillMergedCells)
{
if (_mergeCells.MergesValues.ContainsKey(aR))
{
_mergeCells.MergesValues[aR] = cellValue;
}
else if (_mergeCells.MergesMap.ContainsKey(aR))
{
var mergeKey = _mergeCells.MergesMap[aR];
object mergeValue = null;
if (_mergeCells.MergesValues.ContainsKey(mergeKey))
mergeValue = _mergeCells.MergesValues[mergeKey];
cellValue = mergeValue;
}
}
////2022-09-24跳过endcell结束单元格所以在的列
//跳过startcell起始单元格所在的列
if (columnIndex < startColumnIndex || columnIndex > endColumnIndex && endColumnIndex > 0)
continue;
if (!string.IsNullOrEmpty(aS)) // if c with s meaning is custom style need to check type by xl/style.xml
{
int xfIndex = -1;
if (int.TryParse(aS, NumberStyles.Any, CultureInfo.InvariantCulture, out var styleIndex))
xfIndex = styleIndex;
// only when have s attribute then load styles xml data
if (_style == null)
_style = new ExcelOpenXmlStyles(_archive);
cellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue);
SetCellsValueAndHeaders(cellValue, useHeaderRow, ref headRows, ref isFirstRow, ref cell, columnIndex);
}
else
{
SetCellsValueAndHeaders(cellValue, useHeaderRow, ref headRows, ref isFirstRow, ref cell, columnIndex);
}
}
else if (!XmlReaderHelper.SkipContent(reader))
break;
}
if (isFirstRow)
{
isFirstRow = false; // for startcell logic
if (useHeaderRow)
continue;
}
yield return cell;
}
}
else if (!XmlReaderHelper.SkipContent(reader))
{
break;
}
}
}
else if (!XmlReaderHelper.SkipContent(reader))
{
break;
}
}
}
}
public IEnumerable<T> QueryRange<T>(string sheetName, string startCell, string endCell) where T : class, new()
{
return ExcelOpenXmlSheetReader.QueryImplRange<T>(QueryRange(false, sheetName, startCell, endCell), startCell, endCell, this._config);
}
public static IEnumerable<T> QueryImplRange<T>(IEnumerable<IDictionary<string, object>> values, string startCell, string endCell, Configuration configuration) where T : class, new()
{
var type = typeof(T);
List<ExcelColumnInfo> props = null;
//TODO:need to optimize
string[] headers = null;
Dictionary<string, int> headersDic = null;
string[] keys = null;
var first = true;
var rowIndex = 0;
foreach (var item in values)
{
if (first)
{
keys = item.Keys.ToArray();//.Select((s, i) => new { s,i}).ToDictionary(_=>_.s,_=>_.i);
headers = item?.Values?.Select(s => s?.ToString())?.ToArray(); //TODO:remove
headersDic = headers.Select((o, i) => new { o = (o == null ? string.Empty : o), i })
.OrderBy(x => x.i)
.GroupBy(x => x.o)
.Select(group => new { Group = group, Count = group.Count() })
.SelectMany(groupWithCount =>
groupWithCount.Group.Select(b => b)
.Zip(
Enumerable.Range(1, groupWithCount.Count),
(j, i) => new { key = (i == 1 ? j.o : $"{j.o}_____{i}"), idx = j.i, RowNumber = i }
)
).ToDictionary(_ => _.key, _ => _.idx);
//TODO: alert don't duplicate column name
props = CustomPropertyHelper.GetExcelCustomPropertyInfos(type, keys, configuration);
first = false;
continue;
}
var v = new T();
foreach (var pInfo in props)
{
if (pInfo.ExcelColumnAliases != null)
{
foreach (var alias in pInfo.ExcelColumnAliases)
{
if (headersDic.ContainsKey(alias))
{
object newV = null;
object itemValue = item[keys[headersDic[alias]]];
if (itemValue == null)
continue;
newV = TypeHelper.TypeMapping(v, pInfo, newV, itemValue, rowIndex, startCell, configuration);
}
}
}
//Q: Why need to check every time? A: it needs to check everytime, because it's dictionary
{
object newV = null;
object itemValue = null;
if (pInfo.ExcelIndexName != null && keys.Contains(pInfo.ExcelIndexName))
itemValue = item[pInfo.ExcelIndexName];
else if (headersDic.ContainsKey(pInfo.ExcelColumnName))
itemValue = item[keys[headersDic[pInfo.ExcelColumnName]]];
if (itemValue == null)
continue;
newV = TypeHelper.TypeMapping(v, pInfo, newV, itemValue, rowIndex, startCell, configuration);
}
}
rowIndex++;
yield return v;
}
}
public async Task<IEnumerable<IDictionary<string, object>>> QueryAsyncRange(bool UseHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default(CancellationToken))
{
return await Task.Run(() => Query(UseHeaderRow, sheetName, startCell), cancellationToken).ConfigureAwait(false);
}
public async Task<IEnumerable<T>> QueryAsyncRange<T>(string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default(CancellationToken)) where T : class, new()
{
return await Task.Run(() => Query<T>(sheetName, startCell), cancellationToken).ConfigureAwait(false);
}
#endregion ReaderRange
}
}