- [New] Query support ExcelInvalidCastException to store column, row, value data #309

- [Opt] Query code refactoring
This commit is contained in:
Wei 2022-01-22 11:32:43 +08:00
parent 0c179c87a1
commit 09ec6fd124
12 changed files with 106 additions and 54 deletions

View File

@ -23,6 +23,7 @@
- [New] SaveAs support to convert byte[] value to base64 string
- [New] Query support to convert base64 value to byte[]
- [New] OpenXmlConfiguration add `ConvertByteArrayToBase64String` to turn on/off base64 convertor
- [New] Query support ExcelInvalidCastException to store column, row index #309
### 0.20.0
- [New] SaveAs support image #304

View File

@ -28,6 +28,7 @@
- [New] SaveAs 支持预设转换byte[] 值为 base64 字串
- [New] Query 支持转换 base64 字串值为 bytep[]
- [New] OpenXmlConfiguration 增加 `ConvertByteArrayToBase64String` 属性来开关 base64 转换器
- [New] Query 支持 ExcelInvalidCastException 储存行、列、值数据 #309
### 0.20.0
- [New] SaveAs 支持图片生成 #304

View File

@ -22,6 +22,7 @@
- [New] SaveAs 支持預設轉換byte[] 值為 base64 字串
- [New] Query 支持轉換 base64 字串值為 bytep[]
- [New] OpenXmlConfiguration 增加 `ConvertByteArrayToBase64String` 屬性來開關 base64 轉換器
- [New] Query 支持 ExcelInvalidCastException 儲存行、列、值數據 #309
### 0.20.0
- [New] SaveAs 支持圖片生成 #304

Binary file not shown.

View File

@ -1,6 +1,7 @@
using MiniExcelLibs.Utils;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniExcelLibs.Exceptions
{
public class ExcelInvalidCastException : InvalidCastException
{
public string ColumnName { get; set; }
public int Row { get; set; }
public object Value { get; set; }
public Type InvalidCastType { get; set; }
public ExcelInvalidCastException(string columnName, int row,object value,Type invalidCastType, string message) : base(message)
{
ColumnName = columnName;
Row = row;
Value = value;
InvalidCastType = invalidCastType;
}
}
}

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading.Tasks;
namespace MiniExcelLibs

View File

@ -75,7 +75,7 @@
/// </summary>
public static Task<DataTable> QueryAsDataTableAsync(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
return Task.Run(() => ExcelOpenXmlSheetReader.QueryAsDataTableImpl(stream, useHeaderRow, ref sheetName, excelType, startCell, configuration));
return Task.Run(() => QueryAsDataTable(stream, useHeaderRow, sheetName, excelType, startCell, configuration));
}
}
}

View File

@ -77,15 +77,49 @@
public static DataTable QueryAsDataTable(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
using (var stream = FileHelper.OpenSharedRead(path))
return QueryAsDataTable(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration);
{
return QueryAsDataTable(stream, useHeaderRow, sheetName, excelType:ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration);
}
}
/// <summary>
/// QueryAsDataTable is not recommended, because it'll load all data into memory.
/// </summary>
public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
{
return ExcelOpenXmlSheetReader.QueryAsDataTableImpl(stream, useHeaderRow, ref sheetName, excelType, startCell, configuration);
if (sheetName == null && excelType != ExcelType.CSV) /*Issue #279*/
sheetName = stream.GetSheetNames().First();
var dt = new DataTable(sheetName);
var first = true;
var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, startCell, configuration);
var keys = new List<string>();
foreach (IDictionary<string, object> row in rows)
{
if (first)
{
foreach (var key in row.Keys)
{
if (!string.IsNullOrEmpty(key)) // avoid #298 : Column '' does not belong to table
{
var column = new DataColumn(key, typeof(object)) { Caption = key };
dt.Columns.Add(column);
keys.Add(key);
}
}
dt.BeginLoadData();
first = false;
}
var newRow = dt.NewRow();
foreach (var key in keys)
{
newRow[key] = row[key]; //TODO: optimize not using string key
}
dt.Rows.Add(newRow);
}
dt.EndLoadData();
return dt;
}
public static List<string> GetSheetNames(string path)
@ -97,7 +131,7 @@
public static List<string> GetSheetNames(this Stream stream)
{
var archive = new ExcelOpenXmlZip(stream);
return ExcelOpenXmlSheetReader.GetWorkbookRels(archive.entries).Select(s => s.Name).ToList();
return new ExcelOpenXmlSheetReader(stream).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

@ -368,7 +368,7 @@ namespace MiniExcelLibs.OpenXml
}
}
private static IDictionary<string, object> GetCell(bool useHeaderRow, int maxColumnIndex, Dictionary<int, string> headRows, int startColumnIndex)
private IDictionary<string, object> GetCell(bool useHeaderRow, int maxColumnIndex, Dictionary<int, string> headRows, int startColumnIndex)
{
return useHeaderRow ? CustomPropertyHelper.GetEmptyExpandoObject(headRows) : CustomPropertyHelper.GetEmptyExpandoObject(maxColumnIndex, startColumnIndex);
}
@ -505,7 +505,7 @@ namespace MiniExcelLibs.OpenXml
_sheetRecords = GetWorkbookRels(entries);
}
internal static IEnumerable<SheetRecord> ReadWorkbook(ReadOnlyCollection<ZipArchiveEntry> entries)
internal IEnumerable<SheetRecord> ReadWorkbook(ReadOnlyCollection<ZipArchiveEntry> entries)
{
using (var stream = entries.Single(w => w.FullName == "xl/workbook.xml").Open())
using (XmlReader reader = XmlReader.Create(stream, _xmlSettings))
@ -548,7 +548,7 @@ namespace MiniExcelLibs.OpenXml
}
}
internal static List<SheetRecord> GetWorkbookRels(ReadOnlyCollection<ZipArchiveEntry> entries)
internal List<SheetRecord> GetWorkbookRels(ReadOnlyCollection<ZipArchiveEntry> entries)
{
var sheetRecords = ReadWorkbook(entries).ToList();
@ -587,48 +587,6 @@ namespace MiniExcelLibs.OpenXml
return sheetRecords;
}
internal static DataTable QueryAsDataTableImpl(Stream stream, bool useHeaderRow, ref string sheetName, ExcelType excelType, string startCell, IConfiguration configuration)
{
if (sheetName == null && excelType != ExcelType.CSV) /*Issue #279*/
sheetName = stream.GetSheetNames().First();
var dt = new DataTable(sheetName);
var first = true;
var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, startCell, configuration);
var keys = new List<string>();
foreach (IDictionary<string, object> row in rows)
{
if (first)
{
foreach (var key in row.Keys)
{
if (!string.IsNullOrEmpty(key)) // avoid #298 : Column '' does not belong to table
{
var column = new DataColumn(key, typeof(object)) { Caption = key };
dt.Columns.Add(column);
keys.Add(key);
}
}
dt.BeginLoadData();
first = false;
}
var newRow = dt.NewRow();
foreach (var key in keys)
{
newRow[key] = row[key]; //TODO: optimize not using string key
}
dt.Rows.Add(newRow);
}
dt.EndLoadData();
return dt;
}
private object ReadCellAndSetColumnIndex(XmlReader reader, ref int columnIndex, bool withoutCR, int startColumnIndex, string aR, string aT)
{
var newColumnIndex = 0;

View File

@ -1,6 +1,7 @@
namespace MiniExcelLibs.Utils
{
using MiniExcelLibs.Attributes;
using MiniExcelLibs.Exceptions;
using System;
using System.Collections.Generic;
using System.Data;
@ -70,7 +71,7 @@
var columnName = pInfo.ExcelColumnName ?? pInfo.Property.Name;
var startRowIndex = ReferenceHelper.ConvertCellToXY(startCell).Item2;
var errorRow = startRowIndex + rowIndex + 1;
throw new InvalidCastException($"ColumnName : {columnName}, CellRow : {errorRow}, Value : {itemValue}, it can't cast to {pInfo.Property.PropertyType.Name} type.");
throw new ExcelInvalidCastException(columnName, errorRow, itemValue, pInfo.Property.PropertyType, $"ColumnName : {columnName}, CellRow : {errorRow}, Value : {itemValue}, it can't cast to {pInfo.Property.PropertyType.Name} type.");
}
}

View File

@ -17,6 +17,7 @@ using Xunit;
using Xunit.Abstractions;
using static MiniExcelLibs.Tests.MiniExcelOpenXmlTests;
using System.Collections;
using MiniExcelLibs.Exceptions;
namespace MiniExcelLibs.Tests
{
@ -28,6 +29,35 @@ namespace MiniExcelLibs.Tests
this.output = output;
}
/// <summary>
/// Query type conversion error
/// https://github.com/shps951023/MiniExcel/issues/309
/// </summary>
[Fact]
public void TestIssue209()
{
try
{
var path = PathHelper.GetFile("xlsx/TestIssue309.xlsx");
var rows = MiniExcel.Query<TestIssue209Dto>(path).ToList();
}
catch (ExcelInvalidCastException ex)
{
Assert.Equal("SEQ", ex.ColumnName);
Assert.Equal(4, ex.Row);
Assert.Equal("Error", ex.Value);
Assert.Equal(typeof(int), ex.InvalidCastType);
Assert.Equal("ColumnName : SEQ, CellRow : 4, Value : Error, it can't cast to Int32 type.",ex.Message);
}
}
public class TestIssue209Dto
{
public int ID { get; set; }
public string Name { get; set; }
public int SEQ { get; set; }
}
/// <summary>
/// [SaveAs and Query support btye[] base64 converter · Issue #318 · shps951023/MiniExcel](https://github.com/shps951023/MiniExcel/issues/318)
/// </summary>