mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-12-02 03:47:41 +08:00
- [New] Query support ExcelInvalidCastException to store column, row, value data #309
- [Opt] Query code refactoring
This commit is contained in:
parent
0c179c87a1
commit
09ec6fd124
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
BIN
samples/xlsx/TestIssue309.xlsx
Normal file
BIN
samples/xlsx/TestIssue309.xlsx
Normal file
Binary file not shown.
@ -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;
|
||||
|
23
src/MiniExcel/Exceptions/ExcelInvalidCastException.cs
Normal file
23
src/MiniExcel/Exceptions/ExcelInvalidCastException.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MiniExcelLibs
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user