mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 18:38:08 +08:00
Merge branch 'master' of https://github.com/shps951023/MiniExcel
This commit is contained in:
commit
4a480727c2
58
README.md
58
README.md
@ -20,11 +20,6 @@
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 01/13 希望能更換掉民進黨
|
||||
|
||||
---
|
||||
|
||||
### Introduction
|
||||
|
||||
MiniExcel is simple and efficient to avoid OOM's .NET processing Excel tool.
|
||||
@ -1126,7 +1121,42 @@ Since V1.26.0, we can set the attributes of Column dynamically
|
||||
```
|
||||
![image](https://user-images.githubusercontent.com/12729184/164510353-5aecbc4e-c3ce-41e8-b6cf-afd55eb23b68.png)
|
||||
|
||||
#### 8. DynamicSheetAttribute
|
||||
|
||||
Since V1.31.4 we can set the attributes of Sheet dynamically. We can set sheet name and state (visibility).
|
||||
```csharp
|
||||
var configuration = new OpenXmlConfiguration
|
||||
{
|
||||
DynamicSheets = new DynamicExcelSheet[] {
|
||||
new DynamicExcelSheet("usersSheet") { Name = "Users", State = SheetState.Visible },
|
||||
new DynamicExcelSheet("departmentSheet") { Name = "Departments", State = SheetState.Hidden }
|
||||
}
|
||||
};
|
||||
|
||||
var users = new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } };
|
||||
var department = new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } };
|
||||
var sheets = new Dictionary<string, object>
|
||||
{
|
||||
["usersSheet"] = users,
|
||||
["departmentSheet"] = department
|
||||
};
|
||||
|
||||
var path = PathHelper.GetTempPath();
|
||||
MiniExcel.SaveAs(path, sheets, configuration: configuration);
|
||||
```
|
||||
|
||||
We can also use new attribute ExcelSheetAttribute:
|
||||
|
||||
```C#
|
||||
[ExcelSheet(Name = "Departments", State = SheetState.Hidden)]
|
||||
private class DepartmentDto
|
||||
{
|
||||
[ExcelColumn(Name = "ID",Index = 0)]
|
||||
public string ID { get; set; }
|
||||
[ExcelColumn(Name = "Name",Index = 1)]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### Add, Delete, Update
|
||||
|
||||
@ -1688,6 +1718,21 @@ foreach (var sheet in sheets)
|
||||
|
||||
![image](https://user-images.githubusercontent.com/12729184/116199841-2a1f5300-a76a-11eb-90a3-6710561cf6db.png)
|
||||
|
||||
#### Q. How to query or export information about sheet visibility?
|
||||
|
||||
A. `GetSheetInformations` method.
|
||||
|
||||
|
||||
|
||||
```csharp
|
||||
var sheets = MiniExcel.GetSheetInformations(path);
|
||||
foreach (var sheetInfo in sheets)
|
||||
{
|
||||
Console.WriteLine($"sheet index : {sheetInfo.Index} "); // next sheet index - numbered from 0
|
||||
Console.WriteLine($"sheet name : {sheetInfo.Name} "); // sheet name
|
||||
Console.WriteLine($"sheet state : {sheetInfo.State} "); // sheet visibility state - visible / hidden
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Q. Whether to use Count will load all data into the memory?
|
||||
@ -1859,7 +1904,8 @@ Thanks for providing a free All product IDE for this project ([License](https://
|
||||
|
||||
|
||||
|
||||
|
||||
### Benefit
|
||||
Link https://github.com/mini-software/MiniExcel/issues/560#issue-2080619180
|
||||
|
||||
### Contributors
|
||||
|
||||
|
@ -1810,6 +1810,10 @@ public static DataTable QueryAsDataTableWithoutEmptyRow(Stream stream, bool useH
|
||||
|
||||
|
||||
|
||||
### 收益流水
|
||||
目前收益 https://github.com/mini-software/MiniExcel/issues/560#issue-2080619180
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
![](https://contrib.rocks/image?repo=shps951023/MiniExcel)
|
||||
|
@ -1788,6 +1788,9 @@ public static DataTable QueryAsDataTableWithoutEmptyRow(Stream stream, bool useH
|
||||
|
||||
感謝提供免費IDE支持此專案 ([License](https://user-images.githubusercontent.com/12729184/123988233-6ab17c00-d9fa-11eb-8739-2a08c6a4a263.png))
|
||||
|
||||
### 收益流水
|
||||
目前收益 https://github.com/mini-software/MiniExcel/issues/560#issue-2080619180
|
||||
|
||||
|
||||
|
||||
### Contributors
|
||||
|
BIN
samples/xlsx/TestDynamicSheet.xlsx
Normal file
BIN
samples/xlsx/TestDynamicSheet.xlsx
Normal file
Binary file not shown.
BIN
samples/xlsx/TestMultiSheetWithHiddenSheet.xlsx
Normal file
BIN
samples/xlsx/TestMultiSheetWithHiddenSheet.xlsx
Normal file
Binary file not shown.
21
src/MiniExcel/Attributes/ExcelSheetAttribute.cs
Normal file
21
src/MiniExcel/Attributes/ExcelSheetAttribute.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using System;
|
||||
|
||||
namespace MiniExcelLibs.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class ExcelSheetAttribute : Attribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public SheetState State { get; set; } = SheetState.Visible;
|
||||
}
|
||||
|
||||
public class DynamicExcelSheet : ExcelSheetAttribute
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public DynamicExcelSheet(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
namespace MiniExcelLibs
|
||||
{
|
||||
using OpenXml;
|
||||
using Utils;
|
||||
using Zip;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -10,6 +8,8 @@
|
||||
using System.Dynamic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Utils;
|
||||
using Zip;
|
||||
|
||||
public static partial class MiniExcel
|
||||
{
|
||||
@ -147,7 +147,7 @@
|
||||
{
|
||||
ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCells(filePath);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@ -217,6 +217,20 @@
|
||||
return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select(s => s.Name).ToList();
|
||||
}
|
||||
|
||||
public static List<SheetInfo> GetSheetInformations(string path, OpenXmlConfiguration config = null)
|
||||
{
|
||||
using (var stream = FileHelper.OpenSharedRead(path))
|
||||
return GetSheetInformations(stream, config);
|
||||
}
|
||||
|
||||
public static List<SheetInfo> GetSheetInformations(this Stream stream, OpenXmlConfiguration config = null)
|
||||
{
|
||||
config = config ?? OpenXmlConfiguration.DefaultConfig;
|
||||
|
||||
var archive = new ExcelOpenXmlZip(stream);
|
||||
return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select((s, i) => s.ToSheetInfo((uint)i)).ToList();
|
||||
}
|
||||
|
||||
public static ICollection<string> GetColumns(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
|
||||
{
|
||||
using (var stream = FileHelper.OpenSharedRead(path))
|
||||
|
@ -8,7 +8,6 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
@ -25,7 +24,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
private MergeCells _mergeCells;
|
||||
private ExcelOpenXmlStyles _style;
|
||||
private readonly ExcelOpenXmlZip _archive;
|
||||
private OpenXmlConfiguration _config;
|
||||
private readonly OpenXmlConfiguration _config;
|
||||
|
||||
private static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings
|
||||
{
|
||||
@ -56,10 +55,19 @@ namespace MiniExcelLibs.OpenXml
|
||||
if (sheetName != null)
|
||||
{
|
||||
SetWorkbookRels(_archive.entries);
|
||||
var s = _sheetRecords.SingleOrDefault(_ => _.Name == sheetName);
|
||||
if (s == null)
|
||||
var sheetRecord = _sheetRecords.SingleOrDefault(_ => _.Name == sheetName);
|
||||
if (sheetRecord == null && _config.DynamicSheets != null)
|
||||
{
|
||||
var sheetConfig = _config.DynamicSheets.FirstOrDefault(ds => ds.Key == sheetName);
|
||||
if (sheetConfig != null)
|
||||
{
|
||||
sheetRecord = _sheetRecords.SingleOrDefault(_ => _.Name == sheetConfig.Name);
|
||||
}
|
||||
}
|
||||
if (sheetRecord == 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}");
|
||||
|
||||
sheetEntry = sheets.Single(w => w.FullName == $"xl/{sheetRecord.Path}" || w.FullName == $"/xl/{sheetRecord.Path}" || w.FullName == sheetRecord.Path || sheetRecord.Path == $"/{w.FullName}");
|
||||
}
|
||||
else if (sheets.Count() > 1)
|
||||
{
|
||||
@ -402,6 +410,14 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
public IEnumerable<T> Query<T>(string sheetName, string startCell) where T : class, new()
|
||||
{
|
||||
if (sheetName == null)
|
||||
{
|
||||
var sheetInfo = CustomPropertyHelper.GetExcellSheetInfo(typeof(T), this._config);
|
||||
if (sheetInfo != null)
|
||||
{
|
||||
sheetName = sheetInfo.ExcelSheetName;
|
||||
}
|
||||
}
|
||||
return ExcelOpenXmlSheetReader.QueryImpl<T>(Query(false, sheetName, startCell), startCell, this._config);
|
||||
}
|
||||
|
||||
@ -562,6 +578,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
yield return new SheetRecord(
|
||||
reader.GetAttribute("name"),
|
||||
reader.GetAttribute("state"),
|
||||
uint.Parse(reader.GetAttribute("sheetId")),
|
||||
XmlReaderHelper.GetAttribute(reader, "id", _relationshiopNs)
|
||||
);
|
||||
|
@ -8,7 +8,6 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -34,6 +33,8 @@ namespace MiniExcelLibs.OpenXml
|
||||
public string Name { get; set; }
|
||||
public int SheetIdx { get; set; }
|
||||
public string Path { get { return $"xl/worksheets/sheet{SheetIdx}.xml"; } }
|
||||
|
||||
public string State { get; set; }
|
||||
}
|
||||
internal class DrawingDto
|
||||
{
|
||||
@ -63,7 +64,8 @@ namespace MiniExcelLibs.OpenXml
|
||||
this._archive = new MiniExcelZipArchive(_stream, ZipArchiveMode.Create, true, _utf8WithBom);
|
||||
this._printHeader = printHeader;
|
||||
this._value = value;
|
||||
_sheets.Add(new SheetDto { Name = sheetName, SheetIdx = 1 }); //TODO:remove
|
||||
var defaultSheetInfo = GetSheetInfos(sheetName);
|
||||
_sheets.Add(defaultSheetInfo.ToDto(1)); //TODO:remove
|
||||
}
|
||||
|
||||
public ExcelOpenXmlSheetWriter()
|
||||
@ -82,12 +84,13 @@ namespace MiniExcelLibs.OpenXml
|
||||
foreach (var sheet in sheets)
|
||||
{
|
||||
sheetId++;
|
||||
var s = new SheetDto { Name = sheet.Key, SheetIdx = sheetId };
|
||||
_sheets.Add(s); //TODO:remove
|
||||
var sheetInfos = GetSheetInfos(sheet.Key);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
|
||||
currentSheetIndex = sheetId;
|
||||
|
||||
CreateSheetXml(sheet.Value, s.Path);
|
||||
CreateSheetXml(sheet.Value, sheetDto.Path);
|
||||
}
|
||||
}
|
||||
else if (_value is DataSet)
|
||||
@ -98,13 +101,13 @@ namespace MiniExcelLibs.OpenXml
|
||||
foreach (DataTable dt in sheets.Tables)
|
||||
{
|
||||
sheetId++;
|
||||
var s = new SheetDto { Name = dt.TableName, SheetIdx = sheetId };
|
||||
_sheets.Add(s); //TODO:remove
|
||||
var sheetPath = s.Path;
|
||||
var sheetInfos = GetSheetInfos(dt.TableName);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
|
||||
currentSheetIndex = sheetId;
|
||||
|
||||
CreateSheetXml(dt, sheetPath);
|
||||
CreateSheetXml(dt, sheetDto.Path);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -119,6 +122,155 @@ namespace MiniExcelLibs.OpenXml
|
||||
_archive.Dispose();
|
||||
}
|
||||
|
||||
|
||||
private void GenerateSheetByEnumerable(MiniExcelStreamWriter writer, IEnumerable values)
|
||||
{
|
||||
var maxColumnIndex = 0;
|
||||
var maxRowIndex = 0;
|
||||
List<ExcelColumnInfo> props = null;
|
||||
string mode = null;
|
||||
|
||||
int? rowCount = null;
|
||||
var collection = values as ICollection;
|
||||
if (collection != null)
|
||||
{
|
||||
rowCount = collection.Count;
|
||||
}
|
||||
else if (!_configuration.FastMode)
|
||||
{
|
||||
// The row count is only required up front when not in fastmode
|
||||
collection = new List<object>(values.Cast<object>());
|
||||
rowCount = collection.Count;
|
||||
}
|
||||
|
||||
// Get the enumerator once to ensure deferred linq execution
|
||||
var enumerator = (collection ?? values).GetEnumerator();
|
||||
|
||||
// Move to the first item in order to inspect the value type and determine whether it is empty
|
||||
var empty = !enumerator.MoveNext();
|
||||
|
||||
if (empty)
|
||||
{
|
||||
// only when empty IEnumerable need to check this issue #133 https://github.com/shps951023/MiniExcel/issues/133
|
||||
var genericType = TypeHelper.GetGenericIEnumerables(values).FirstOrDefault();
|
||||
if (genericType == null || genericType == typeof(object) // sometime generic type will be object, e.g: https://user-images.githubusercontent.com/12729184/132812859-52984314-44d1-4ee8-9487-2d1da159f1f0.png
|
||||
|| typeof(IDictionary<string, object>).IsAssignableFrom(genericType)
|
||||
|| typeof(IDictionary).IsAssignableFrom(genericType))
|
||||
{
|
||||
WriteEmptySheet(writer);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGenericTypePropertiesMode(genericType, ref mode, out maxColumnIndex, out props);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstItem = enumerator.Current;
|
||||
if (firstItem is IDictionary<string, object> genericDic)
|
||||
{
|
||||
mode = "IDictionary<string, object>";
|
||||
props = GetDictionaryColumnInfo(genericDic, null);
|
||||
maxColumnIndex = props.Count;
|
||||
}
|
||||
else if (firstItem is IDictionary dic)
|
||||
{
|
||||
mode = "IDictionary";
|
||||
props = GetDictionaryColumnInfo(null, dic);
|
||||
//maxColumnIndex = dic.Keys.Count;
|
||||
maxColumnIndex = props.Count; // why not using keys, because ignore attribute ![image](https://user-images.githubusercontent.com/12729184/163686902-286abb70-877b-4e84-bd3b-001ad339a84a.png)
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGenericTypePropertiesMode(firstItem.GetType(), ref mode, out maxColumnIndex, out props);
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"" >");
|
||||
|
||||
long dimensionWritePosition = 0;
|
||||
|
||||
// We can write the dimensions directly if the row count is known
|
||||
if (_configuration.FastMode && rowCount == null)
|
||||
{
|
||||
// Write a placeholder for the table dimensions and save thee position for later
|
||||
dimensionWritePosition = writer.WriteAndFlush("<x:dimension ref=\"");
|
||||
writer.Write(" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
maxRowIndex = rowCount.Value + (_printHeader && rowCount > 0 ? 1 : 0);
|
||||
writer.Write($@"<x:dimension ref=""{GetDimensionRef(maxRowIndex, maxColumnIndex)}""/>");
|
||||
}
|
||||
|
||||
//cols:width
|
||||
WriteColumnsWidths(writer, props);
|
||||
|
||||
//header
|
||||
writer.Write($@"<x:sheetData>");
|
||||
var yIndex = 1;
|
||||
var xIndex = 1;
|
||||
if (_printHeader)
|
||||
{
|
||||
PrintHeader(writer, props);
|
||||
yIndex++;
|
||||
}
|
||||
|
||||
if (!empty)
|
||||
{
|
||||
// body
|
||||
if (mode == "IDictionary<string, object>") //Dapper Row
|
||||
maxRowIndex = GenerateSheetByColumnInfo<IDictionary<string, object>>(writer, enumerator, props, xIndex, yIndex);
|
||||
else if (mode == "IDictionary") //IDictionary
|
||||
maxRowIndex = GenerateSheetByColumnInfo<IDictionary>(writer, enumerator, props, xIndex, yIndex);
|
||||
else if (mode == "Properties")
|
||||
maxRowIndex = GenerateSheetByColumnInfo<object>(writer, enumerator, props, xIndex, yIndex);
|
||||
else
|
||||
throw new NotImplementedException($"Type {values.GetType().FullName} is not implemented. Please open an issue.");
|
||||
}
|
||||
|
||||
writer.Write("</x:sheetData>");
|
||||
if (_configuration.AutoFilter)
|
||||
writer.Write($"<x:autoFilter ref=\"{GetDimensionRef(maxRowIndex, maxColumnIndex)}\" />");
|
||||
|
||||
// The dimension has already been written if row count is defined
|
||||
if (_configuration.FastMode && rowCount == null)
|
||||
{
|
||||
// Flush and save position so that we can get back again.
|
||||
var pos = writer.Flush();
|
||||
|
||||
// Seek back and write the dimensions of the table
|
||||
writer.SetPosition(dimensionWritePosition);
|
||||
writer.WriteAndFlush($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}""");
|
||||
writer.SetPosition(pos);
|
||||
}
|
||||
|
||||
writer.Write("<x:drawing r:id=\"drawing" + currentSheetIndex + "\" /></x:worksheet>");
|
||||
}
|
||||
|
||||
private static void PrintHeader(MiniExcelStreamWriter writer, List<ExcelColumnInfo> props)
|
||||
{
|
||||
var xIndex = 1;
|
||||
var yIndex = 1;
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
|
||||
foreach (var p in props)
|
||||
{
|
||||
if (p == null)
|
||||
{
|
||||
xIndex++; //reason : https://github.com/shps951023/MiniExcel/issues/142
|
||||
continue;
|
||||
}
|
||||
|
||||
var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex);
|
||||
WriteC(writer, r, columnName: p.ExcelColumnName);
|
||||
xIndex++;
|
||||
}
|
||||
|
||||
writer.Write("</x:row>");
|
||||
}
|
||||
|
||||
private void CreateSheetXml(object value, string sheetPath)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(sheetPath, CompressionLevel.Fastest);
|
||||
@ -131,10 +283,6 @@ namespace MiniExcelLibs.OpenXml
|
||||
goto End; //for re-using code
|
||||
}
|
||||
|
||||
var type = value.GetType();
|
||||
|
||||
Type genericType = null;
|
||||
|
||||
//DapperRow
|
||||
|
||||
if (value is IDataReader)
|
||||
@ -143,137 +291,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
else if (value is IEnumerable)
|
||||
{
|
||||
var values = value as IEnumerable;
|
||||
|
||||
// try to get type from reflection
|
||||
// genericType = null
|
||||
|
||||
var rowCount = 0;
|
||||
|
||||
var maxColumnIndex = 0;
|
||||
//List<object> keys = new List<object>();
|
||||
List<ExcelColumnInfo> props = null;
|
||||
string mode = null;
|
||||
|
||||
// reason : https://stackoverflow.com/questions/66797421/how-replace-top-format-mark-after-MiniExcelStreamWriter-writing
|
||||
// check mode & get maxRowCount & maxColumnIndex
|
||||
{
|
||||
foreach (var item in values) //TODO: need to optimize
|
||||
{
|
||||
rowCount = checked(rowCount + 1);
|
||||
|
||||
//TODO: if item is null but it's collection<T>, it can get T type from reflection
|
||||
if (item != null && mode == null)
|
||||
{
|
||||
if (item is IDictionary<string, object>)
|
||||
{
|
||||
mode = "IDictionary<string, object>";
|
||||
var dic = item as IDictionary<string, object>;
|
||||
props = GetDictionaryColumnInfo(dic, null);
|
||||
maxColumnIndex = props.Count;
|
||||
}
|
||||
else if (item is IDictionary)
|
||||
{
|
||||
var dic = item as IDictionary;
|
||||
mode = "IDictionary";
|
||||
props = GetDictionaryColumnInfo(null, dic);
|
||||
//maxColumnIndex = dic.Keys.Count;
|
||||
maxColumnIndex = props.Count; // why not using keys, because ignore attribute ![image](https://user-images.githubusercontent.com/12729184/163686902-286abb70-877b-4e84-bd3b-001ad339a84a.png)
|
||||
}
|
||||
else
|
||||
{
|
||||
var _t = item.GetType();
|
||||
if (_t != genericType)
|
||||
genericType = item.GetType();
|
||||
genericType = item.GetType();
|
||||
SetGenericTypePropertiesMode(genericType, ref mode, out maxColumnIndex, out props);
|
||||
}
|
||||
|
||||
var collection = value as ICollection;
|
||||
if (collection != null)
|
||||
{
|
||||
rowCount = checked((value as ICollection).Count);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rowCount == 0)
|
||||
{
|
||||
// only when empty IEnumerable need to check this issue #133 https://github.com/shps951023/MiniExcel/issues/133
|
||||
genericType = TypeHelper.GetGenericIEnumerables(values).FirstOrDefault();
|
||||
if (genericType == null || genericType == typeof(object) // sometime generic type will be object, e.g: https://user-images.githubusercontent.com/12729184/132812859-52984314-44d1-4ee8-9487-2d1da159f1f0.png
|
||||
|| typeof(IDictionary<string, object>).IsAssignableFrom(genericType)
|
||||
|| typeof(IDictionary).IsAssignableFrom(genericType))
|
||||
{
|
||||
WriteEmptySheet(writer);
|
||||
goto End; //for re-using code
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGenericTypePropertiesMode(genericType, ref mode, out maxColumnIndex, out props);
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"" >");
|
||||
|
||||
// dimension
|
||||
var maxRowIndex = rowCount + (_printHeader && rowCount > 0 ? 1 : 0); //TODO:it can optimize
|
||||
writer.Write($@"<x:dimension ref=""{GetDimensionRef(maxRowIndex, maxColumnIndex)}""/>");
|
||||
|
||||
//cols:width
|
||||
var ecwProp = props?.Where(x => x?.ExcelColumnWidth != null).ToList();
|
||||
if (ecwProp != null && ecwProp.Count > 0)
|
||||
{
|
||||
writer.Write($@"<x:cols>");
|
||||
foreach (var p in ecwProp)
|
||||
{
|
||||
writer.Write($@"<x:col min=""{p.ExcelColumnIndex + 1}"" max=""{p.ExcelColumnIndex + 1}"" width=""{p.ExcelColumnWidth}"" customWidth=""1"" />");
|
||||
}
|
||||
writer.Write($@"</x:cols>");
|
||||
}
|
||||
|
||||
//header
|
||||
writer.Write($@"<x:sheetData>");
|
||||
var yIndex = 1;
|
||||
var xIndex = 1;
|
||||
if (_printHeader)
|
||||
{
|
||||
var cellIndex = xIndex;
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
|
||||
foreach (var p in props)
|
||||
{
|
||||
if (p == null)
|
||||
{
|
||||
cellIndex++; //reason : https://github.com/shps951023/MiniExcel/issues/142
|
||||
continue;
|
||||
}
|
||||
|
||||
var r = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, yIndex);
|
||||
WriteC(writer, r, columnName: p.ExcelColumnName);
|
||||
cellIndex++;
|
||||
}
|
||||
|
||||
writer.Write($"</x:row>");
|
||||
yIndex++;
|
||||
}
|
||||
|
||||
// body
|
||||
if (mode == "IDictionary<string, object>") //Dapper Row
|
||||
GenerateSheetByColumnInfo<IDictionary<string, object>>(writer, value as IEnumerable, props, xIndex, yIndex);
|
||||
else if (mode == "IDictionary") //IDictionary
|
||||
GenerateSheetByColumnInfo<IDictionary>(writer, value as IEnumerable, props, xIndex, yIndex);
|
||||
else if (mode == "Properties")
|
||||
GenerateSheetByColumnInfo<object>(writer, value as IEnumerable, props, xIndex, yIndex);
|
||||
else
|
||||
throw new NotImplementedException($"Type {type.Name} & genericType {genericType.Name} not Implemented. please issue for me.");
|
||||
writer.Write("</x:sheetData>");
|
||||
if (_configuration.AutoFilter)
|
||||
writer.Write($"<x:autoFilter ref=\"A1:{ExcelOpenXmlUtils.ConvertXyToCell(maxColumnIndex, maxRowIndex == 0 ? 1 : maxRowIndex)}\" />");
|
||||
writer.Write("<x:drawing r:id=\"drawing" + currentSheetIndex + "\" /></x:worksheet>");
|
||||
GenerateSheetByEnumerable(writer, value as IEnumerable);
|
||||
}
|
||||
else if (value is DataTable)
|
||||
{
|
||||
@ -281,7 +299,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Type {type.Name} & genericType {genericType.Name} not Implemented. please issue for me.");
|
||||
throw new NotImplementedException($"Type {value.GetType().FullName} is not implemented. Please open an issue.");
|
||||
}
|
||||
}
|
||||
End: //for re-using code
|
||||
@ -353,12 +371,15 @@ namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main""><x:dimension ref=""A1""/><x:sheetData></x:sheetData></x:worksheet>");
|
||||
}
|
||||
private void GenerateSheetByColumnInfo<T>(MiniExcelStreamWriter writer, IEnumerable value, List<ExcelColumnInfo> props, int xIndex = 1, int yIndex = 1)
|
||||
private int GenerateSheetByColumnInfo<T>(MiniExcelStreamWriter writer, IEnumerator value, List<ExcelColumnInfo> props, int xIndex = 1, int yIndex = 1)
|
||||
{
|
||||
var isDic = typeof(T) == typeof(IDictionary);
|
||||
var isDapperRow = typeof(T) == typeof(IDictionary<string, object>);
|
||||
foreach (T v in value)
|
||||
do
|
||||
{
|
||||
// The enumerator has already moved to the first item
|
||||
T v = (T)value.Current;
|
||||
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
var cellIndex = xIndex;
|
||||
foreach (var p in props)
|
||||
@ -390,7 +411,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
writer.Write($"</x:row>");
|
||||
yIndex++;
|
||||
}
|
||||
} while (value.MoveNext());
|
||||
|
||||
return yIndex - 1;
|
||||
}
|
||||
|
||||
private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo p)
|
||||
@ -560,18 +583,30 @@ namespace MiniExcelLibs.OpenXml
|
||||
// dimension
|
||||
var maxRowIndex = value.Rows.Count + (_printHeader && value.Rows.Count > 0 ? 1 : 0);
|
||||
var maxColumnIndex = value.Columns.Count;
|
||||
writer.Write($@"<x:dimension ref=""{GetDimensionRef(maxRowIndex, maxColumnIndex)}""/><x:sheetData>");
|
||||
writer.Write($@"<x:dimension ref=""{GetDimensionRef(maxRowIndex, maxColumnIndex)}""/>");
|
||||
|
||||
var props = new List<ExcelColumnInfo>();
|
||||
for (var i = 0; i < value.Columns.Count; i++)
|
||||
{
|
||||
var columnName = value.Columns[i].Caption ?? value.Columns[i].ColumnName;
|
||||
var prop = GetColumnInfosFromDynamicConfiguration(columnName);
|
||||
props.Add(prop);
|
||||
}
|
||||
|
||||
WriteColumnsWidths(writer, props);
|
||||
|
||||
writer.Write("<x:sheetData>");
|
||||
if (_printHeader)
|
||||
{
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
var xIndex = xy.Item1;
|
||||
foreach (DataColumn c in value.Columns)
|
||||
foreach (var p in props)
|
||||
{
|
||||
var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex);
|
||||
WriteC(writer, r, columnName: c.Caption ?? c.ColumnName);
|
||||
WriteC(writer, r, columnName: p.ExcelColumnName);
|
||||
xIndex++;
|
||||
}
|
||||
|
||||
writer.Write($"</x:row>");
|
||||
yIndex++;
|
||||
}
|
||||
@ -596,11 +631,12 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
private void GenerateSheetByIDataReader(MiniExcelStreamWriter writer, IDataReader reader)
|
||||
{
|
||||
var xy = ExcelOpenXmlUtils.ConvertCellToXY("A1"); /*TODO:code smell*/
|
||||
long dimensionWritePosition = 0;
|
||||
writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">");
|
||||
var yIndex = xy.Item2;
|
||||
var xIndex = 0;
|
||||
var xIndex = 1;
|
||||
var yIndex = 1;
|
||||
var maxColumnIndex = 0;
|
||||
var maxRowIndex = 0;
|
||||
{
|
||||
|
||||
if (_configuration.FastMode)
|
||||
@ -613,9 +649,10 @@ namespace MiniExcelLibs.OpenXml
|
||||
for (var i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
var columnName = reader.GetName(i);
|
||||
var prop = GetColumnInfosForIDataReader(columnName);
|
||||
var prop = GetColumnInfosFromDynamicConfiguration(columnName);
|
||||
props.Add(prop);
|
||||
}
|
||||
maxColumnIndex = props.Count;
|
||||
|
||||
WriteColumnsWidths(writer, props);
|
||||
|
||||
@ -623,23 +660,14 @@ namespace MiniExcelLibs.OpenXml
|
||||
int fieldCount = reader.FieldCount;
|
||||
if (_printHeader)
|
||||
{
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
xIndex = xy.Item1;
|
||||
foreach (var p in props)
|
||||
{
|
||||
var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex);
|
||||
WriteC(writer, r, columnName: p.ExcelColumnName);
|
||||
xIndex++;
|
||||
}
|
||||
writer.Write($"</x:row>");
|
||||
PrintHeader(writer, props);
|
||||
yIndex++;
|
||||
}
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
writer.Write($"<x:row r=\"{yIndex}\">");
|
||||
xIndex = xy.Item1;
|
||||
|
||||
xIndex = 1;
|
||||
for (int i = 0; i < fieldCount; i++)
|
||||
{
|
||||
var cellValue = reader.GetValue(i);
|
||||
@ -649,20 +677,23 @@ namespace MiniExcelLibs.OpenXml
|
||||
writer.Write($"</x:row>");
|
||||
yIndex++;
|
||||
}
|
||||
|
||||
// Subtract 1 because cell indexing starts with 1
|
||||
maxRowIndex = yIndex - 1;
|
||||
}
|
||||
writer.Write("</x:sheetData>");
|
||||
if (_configuration.AutoFilter)
|
||||
writer.Write($"<x:autoFilter ref=\"A1:{ExcelOpenXmlUtils.ConvertXyToCell((xIndex - 1)/*TODO:code smell*/, yIndex - 1)}\" />");
|
||||
writer.Write($"<x:autoFilter ref=\"{GetDimensionRef(maxRowIndex, maxColumnIndex)}\" />");
|
||||
writer.WriteAndFlush("</x:worksheet>");
|
||||
|
||||
if (_configuration.FastMode)
|
||||
{
|
||||
writer.SetPosition(dimensionWritePosition);
|
||||
writer.WriteAndFlush($@"A1:{ExcelOpenXmlUtils.ConvertXyToCell((xIndex - 1)/*TODO:code smell*/, yIndex - 1)}""");
|
||||
writer.WriteAndFlush($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}""");
|
||||
}
|
||||
}
|
||||
|
||||
private ExcelColumnInfo GetColumnInfosForIDataReader(string columnName)
|
||||
private ExcelColumnInfo GetColumnInfosFromDynamicConfiguration(string columnName)
|
||||
{
|
||||
var prop = new ExcelColumnInfo
|
||||
{
|
||||
@ -695,6 +726,31 @@ namespace MiniExcelLibs.OpenXml
|
||||
return prop;
|
||||
}
|
||||
|
||||
private ExcellSheetInfo GetSheetInfos(string sheetName)
|
||||
{
|
||||
var info = new ExcellSheetInfo
|
||||
{
|
||||
ExcelSheetName = sheetName,
|
||||
Key = sheetName,
|
||||
ExcelSheetState = SheetState.Visible
|
||||
};
|
||||
|
||||
if (_configuration.DynamicSheets == null || _configuration.DynamicSheets.Length <= 0)
|
||||
return info;
|
||||
|
||||
var dynamicSheet = _configuration.DynamicSheets.SingleOrDefault(_ => _.Key == sheetName);
|
||||
if (dynamicSheet == null)
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
if (dynamicSheet.Name != null)
|
||||
info.ExcelSheetName = dynamicSheet.Name;
|
||||
info.ExcelSheetState = dynamicSheet.State;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable<ExcelColumnInfo> props)
|
||||
{
|
||||
var ecwProps = props.Where(x => x?.ExcelColumnWidth != null).ToList();
|
||||
@ -812,7 +868,14 @@ namespace MiniExcelLibs.OpenXml
|
||||
foreach (var s in _sheets)
|
||||
{
|
||||
sheetId++;
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{s.Name}"" sheetId=""{sheetId}"" r:id=""{s.ID}"" />");
|
||||
if (string.IsNullOrEmpty(s.State))
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{s.Name}"" sheetId=""{sheetId}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{s.Name}"" sheetId=""{sheetId}"" state=""{s.State}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
workbookRelsXml.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"" Target=""/{s.Path}"" Id=""{s.ID}"" />");
|
||||
|
||||
//TODO: support multiple drawing
|
||||
|
@ -8,7 +8,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly Encoding _encoding;
|
||||
internal readonly StreamWriter _streamWriter;
|
||||
private readonly StreamWriter _streamWriter;
|
||||
private bool disposedValue;
|
||||
public MiniExcelStreamWriter(Stream stream,Encoding encoding, int bufferSize)
|
||||
{
|
||||
@ -30,6 +30,12 @@ namespace MiniExcelLibs.OpenXml
|
||||
return this._streamWriter.BaseStream.Position;
|
||||
}
|
||||
|
||||
public long Flush()
|
||||
{
|
||||
this._streamWriter.Flush();
|
||||
return this._streamWriter.BaseStream.Position;
|
||||
}
|
||||
|
||||
public void SetPosition(long position)
|
||||
{
|
||||
this._streamWriter.BaseStream.Position = position;
|
||||
|
@ -1,10 +1,4 @@
|
||||
|
||||
using MiniExcelLibs.Utils;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.Attributes;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
@ -19,5 +13,6 @@ namespace MiniExcelLibs.OpenXml
|
||||
public bool EnableWriteNullValueCell { get; set; } = true;
|
||||
public bool EnableSharedStringCache { get; set; } = true;
|
||||
public long SharedStringCacheSize { get; set; } = 5 * 1024 * 1024;
|
||||
public DynamicExcelSheet[] DynamicSheets { get; set; }
|
||||
}
|
||||
}
|
32
src/MiniExcel/OpenXml/SheetInfo.cs
Normal file
32
src/MiniExcel/OpenXml/SheetInfo.cs
Normal file
@ -0,0 +1,32 @@
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
public class SheetInfo
|
||||
{
|
||||
public SheetInfo(uint id, uint index, string name, SheetState sheetState)
|
||||
{
|
||||
Id = id;
|
||||
Index = index;
|
||||
Name = name;
|
||||
State = sheetState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal sheet id - depends on the order in which the sheet is added
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
/// <summary>
|
||||
/// Next sheet index - numbered from 0
|
||||
/// </summary>
|
||||
public uint Index { get; }
|
||||
/// <summary>
|
||||
/// Sheet name
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Sheet visibility state
|
||||
/// </summary>
|
||||
public SheetState State { get; }
|
||||
}
|
||||
|
||||
public enum SheetState { Visible, Hidden, VeryHidden }
|
||||
}
|
@ -1,20 +1,38 @@
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
using System;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
internal sealed class SheetRecord
|
||||
{
|
||||
public SheetRecord(string name, uint id, string rid)
|
||||
public SheetRecord(string name, string state, uint id, string rid)
|
||||
{
|
||||
Name = name;
|
||||
State = state;
|
||||
Id = id;
|
||||
Rid = rid;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string State { get; set; }
|
||||
|
||||
public uint Id { get; }
|
||||
|
||||
public string Rid { get; set; }
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public SheetInfo ToSheetInfo(uint index)
|
||||
{
|
||||
if (string.IsNullOrEmpty(State))
|
||||
{
|
||||
return new SheetInfo(Id, index, Name, SheetState.Visible);
|
||||
}
|
||||
if (Enum.TryParse(State, true, out SheetState stateEnum))
|
||||
{
|
||||
return new SheetInfo(Id, index, Name, stateEnum);
|
||||
}
|
||||
throw new ArgumentException($"Unable to parse sheet state. Sheet name: {Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
namespace MiniExcelLibs.Utils
|
||||
{
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
@ -23,6 +23,26 @@
|
||||
public bool ExcelIgnore { get; internal set; }
|
||||
}
|
||||
|
||||
internal class ExcellSheetInfo
|
||||
{
|
||||
public object Key { get; set; }
|
||||
public string ExcelSheetName { get; set; }
|
||||
public SheetState ExcelSheetState { get; set; }
|
||||
|
||||
private string ExcelSheetStateAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return ExcelSheetState.ToString().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
public SheetDto ToDto(int sheetIndex)
|
||||
{
|
||||
return new SheetDto { Name = ExcelSheetName, SheetIdx = sheetIndex, State = ExcelSheetStateAsString };
|
||||
}
|
||||
}
|
||||
|
||||
internal static partial class CustomPropertyHelper
|
||||
{
|
||||
internal static IDictionary<string, object> GetEmptyExpandoObject(int maxColumnIndex, int startCellIndex)
|
||||
@ -142,9 +162,9 @@
|
||||
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
|
||||
typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attributes != null && attributes.Length > 0)
|
||||
if (attributes != null && attributes.Length > 0)
|
||||
return attributes[0].Description;
|
||||
else
|
||||
else
|
||||
return source.ToString();
|
||||
}
|
||||
|
||||
@ -185,7 +205,7 @@
|
||||
ExcelColumnWidth = p.GetAttribute<ExcelColumnWidthAttribute>()?.ExcelColumnWidth ?? excelColumn?.Width,
|
||||
ExcelFormat = excelFormat ?? excelColumn?.Format,
|
||||
};
|
||||
}).Where(_=>_!=null);
|
||||
}).Where(_ => _ != null);
|
||||
}
|
||||
|
||||
private static IEnumerable<ExcelColumnInfo> GetExcelPropertyInfo(Type type, BindingFlags bindingFlags, Configuration configuration)
|
||||
@ -194,6 +214,38 @@
|
||||
return ConvertToExcelCustomPropertyInfo(type.GetProperties(bindingFlags), configuration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static ExcellSheetInfo GetExcellSheetInfo(Type type, Configuration configuration)
|
||||
{
|
||||
// default options
|
||||
var sheetInfo = new ExcellSheetInfo()
|
||||
{
|
||||
Key = type.Name,
|
||||
ExcelSheetName = null, // will be generated automatically as Sheet<Index>
|
||||
ExcelSheetState = SheetState.Visible
|
||||
};
|
||||
|
||||
// options from ExcelSheetAttribute
|
||||
ExcelSheetAttribute excelSheetAttribute = type.GetCustomAttribute(typeof(ExcelSheetAttribute)) as ExcelSheetAttribute;
|
||||
if (excelSheetAttribute != null)
|
||||
{
|
||||
sheetInfo.ExcelSheetName = excelSheetAttribute.Name ?? type.Name;
|
||||
sheetInfo.ExcelSheetState = excelSheetAttribute.State;
|
||||
}
|
||||
|
||||
// options from DynamicSheets configuration
|
||||
OpenXmlConfiguration openXmlCOnfiguration = configuration as OpenXmlConfiguration;
|
||||
if (openXmlCOnfiguration != null && openXmlCOnfiguration.DynamicSheets != null && openXmlCOnfiguration.DynamicSheets.Length > 0)
|
||||
{
|
||||
var dynamicSheet = openXmlCOnfiguration.DynamicSheets.SingleOrDefault(_ => _.Key == type.Name);
|
||||
if (dynamicSheet != null)
|
||||
{
|
||||
sheetInfo.ExcelSheetName = dynamicSheet.Name;
|
||||
sheetInfo.ExcelSheetState = dynamicSheet.State;
|
||||
}
|
||||
}
|
||||
|
||||
return sheetInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,13 +140,13 @@
|
||||
{
|
||||
newValue = XmlEncoder.DecodeString(itemValue?.ToString());
|
||||
}
|
||||
else if (pInfo.Property.Info.PropertyType.IsEnum)
|
||||
else if (pInfo.ExcludeNullableType.IsEnum)
|
||||
{
|
||||
var fieldInfo = pInfo.Property.Info.PropertyType.GetFields().FirstOrDefault(e => e.GetCustomAttribute<DescriptionAttribute>(false)?.Description == itemValue?.ToString());
|
||||
var fieldInfo = pInfo.ExcludeNullableType.GetFields().FirstOrDefault(e => e.GetCustomAttribute<DescriptionAttribute>(false)?.Description == itemValue?.ToString());
|
||||
if (fieldInfo != null)
|
||||
newValue = Enum.Parse(pInfo.Property.Info.PropertyType, fieldInfo.Name, true);
|
||||
newValue = Enum.Parse(pInfo.ExcludeNullableType, fieldInfo.Name, true);
|
||||
else
|
||||
newValue = Enum.Parse(pInfo.Property.Info.PropertyType, itemValue?.ToString(), true);
|
||||
newValue = Enum.Parse(pInfo.ExcludeNullableType, itemValue?.ToString(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,7 +1,10 @@
|
||||
using Xunit;
|
||||
using System.Linq;
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace MiniExcelLibs.Tests
|
||||
{
|
||||
@ -84,7 +87,7 @@ namespace MiniExcelLibs.Tests
|
||||
{
|
||||
var rows = MiniExcel.Query(path, sheetName: sheetName);
|
||||
}
|
||||
|
||||
|
||||
Assert.Equal(new[] { "Sheet2", "Sheet1", "Sheet3" }, sheetNames);
|
||||
}
|
||||
|
||||
@ -100,5 +103,186 @@ namespace MiniExcelLibs.Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ExcelSheet(Name = "Users")]
|
||||
private class UserDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
[ExcelSheet(Name = "Departments", State = SheetState.Hidden)]
|
||||
private class DepartmentDto
|
||||
{
|
||||
public string ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExcelSheetAttributeIsUsedWhenReadExcel()
|
||||
{
|
||||
var path = @"../../../../../samples/xlsx/TestDynamicSheet.xlsx";
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
var users = stream.Query<UserDto>().ToList();
|
||||
Assert.Equal(2, users.Count());
|
||||
Assert.Equal("Jack", users[0].Name);
|
||||
|
||||
var departments = stream.Query<DepartmentDto>().ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
}
|
||||
|
||||
{
|
||||
var users = MiniExcel.Query<UserDto>(path).ToList();
|
||||
Assert.Equal(2, users.Count());
|
||||
Assert.Equal("Jack", users[0].Name);
|
||||
|
||||
var departments = MiniExcel.Query<DepartmentDto>(path).ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DynamicSheetConfigurationIsUsedWhenReadExcel()
|
||||
{
|
||||
var configuration = new OpenXmlConfiguration
|
||||
{
|
||||
DynamicSheets = new[]
|
||||
{
|
||||
new DynamicExcelSheet("usersSheet") { Name = "Users" },
|
||||
new DynamicExcelSheet("departmentSheet") { Name = "Departments" }
|
||||
}
|
||||
};
|
||||
|
||||
var path = @"../../../../../samples/xlsx/TestDynamicSheet.xlsx";
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
// take first sheet as default
|
||||
var users = stream.Query(configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, users.Count());
|
||||
Assert.Equal("Jack", users[0].Name);
|
||||
|
||||
// take second sheet by sheet name
|
||||
var departments = stream.Query(sheetName: "Departments", configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
|
||||
// take second sheet by sheet key
|
||||
departments = stream.Query(sheetName: "departmentSheet", configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
}
|
||||
|
||||
{
|
||||
// take first sheet as default
|
||||
var users = MiniExcel.Query(path, configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, users.Count());
|
||||
Assert.Equal("Jack", users[0].Name);
|
||||
|
||||
// take second sheet by sheet name
|
||||
var departments = MiniExcel.Query(path, sheetName: "Departments", configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
|
||||
// take second sheet by sheet key
|
||||
departments = MiniExcel.Query(path, sheetName: "departmentSheet", configuration: configuration, useHeaderRow: true).ToList();
|
||||
Assert.Equal(2, departments.Count());
|
||||
Assert.Equal("HR", departments[0].Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadSheetVisibilityStateTest()
|
||||
{
|
||||
var path = @"../../../../../samples/xlsx/TestMultiSheetWithHiddenSheet.xlsx";
|
||||
{
|
||||
var sheetInfos = MiniExcel.GetSheetInformations(path).ToList();
|
||||
Assert.Collection(sheetInfos,
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(0u, i.Index);
|
||||
Assert.Equal(2u, i.Id);
|
||||
Assert.Equal(SheetState.Visible, i.State);
|
||||
Assert.Equal("Sheet2", i.Name);
|
||||
},
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(1u, i.Index);
|
||||
Assert.Equal(1u, i.Id);
|
||||
Assert.Equal(SheetState.Visible, i.State);
|
||||
Assert.Equal("Sheet1", i.Name);
|
||||
},
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(2u, i.Index);
|
||||
Assert.Equal(3u, i.Id);
|
||||
Assert.Equal(SheetState.Visible, i.State);
|
||||
Assert.Equal("Sheet3", i.Name);
|
||||
},
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(3u, i.Index);
|
||||
Assert.Equal(5u, i.Id);
|
||||
Assert.Equal(SheetState.Hidden, i.State);
|
||||
Assert.Equal("HiddenSheet4", i.Name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteHiddenSheetTest()
|
||||
{
|
||||
var configuration = new OpenXmlConfiguration
|
||||
{
|
||||
DynamicSheets = new[]
|
||||
{
|
||||
new DynamicExcelSheet("usersSheet")
|
||||
{
|
||||
Name = "Users",
|
||||
State = SheetState.Visible
|
||||
},
|
||||
new DynamicExcelSheet("departmentSheet")
|
||||
{
|
||||
Name = "Departments",
|
||||
State = SheetState.Hidden
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var users = new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } };
|
||||
var department = new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } };
|
||||
var sheets = new Dictionary<string, object>
|
||||
{
|
||||
["usersSheet"] = users,
|
||||
["departmentSheet"] = department
|
||||
};
|
||||
|
||||
string path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
MiniExcel.SaveAs(path, sheets, configuration: configuration);
|
||||
|
||||
var sheetInfos = MiniExcel.GetSheetInformations(path).ToList();
|
||||
Assert.Collection(sheetInfos,
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(0u, i.Index);
|
||||
Assert.Equal(1u, i.Id);
|
||||
Assert.Equal(SheetState.Visible, i.State);
|
||||
Assert.Equal("Users", i.Name);
|
||||
},
|
||||
i =>
|
||||
{
|
||||
Assert.Equal(1u, i.Index);
|
||||
Assert.Equal(2u, i.Id);
|
||||
Assert.Equal(SheetState.Hidden, i.State);
|
||||
Assert.Equal("Departments", i.Name);
|
||||
});
|
||||
|
||||
foreach (var sheetName in sheetInfos.Select(s => s.Name))
|
||||
{
|
||||
var rows = MiniExcel.Query(path, sheetName: sheetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1242,7 +1242,60 @@ namespace MiniExcelLibs.Tests
|
||||
Assert.Contains("Its value", rows[0]);
|
||||
Assert.Contains("Name of something", rows[1]);
|
||||
Assert.Contains("Its value", rows[1]);
|
||||
|
||||
|
||||
Assert.Equal("MiniExcel", rows[0]["Name of something"]);
|
||||
Assert.Equal(1D, rows[0]["Its value"]);
|
||||
Assert.Equal("Github", rows[1]["Name of something"]);
|
||||
Assert.Equal(2D, rows[1]["Its value"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
var table = new DataTable();
|
||||
{
|
||||
table.Columns.Add("Column1", typeof(string));
|
||||
table.Columns.Add("Column2", typeof(int));
|
||||
table.Rows.Add("MiniExcel", 1);
|
||||
table.Rows.Add("Github", 2);
|
||||
}
|
||||
|
||||
var configuration = new OpenXmlConfiguration
|
||||
{
|
||||
DynamicColumns = new[]
|
||||
{
|
||||
new DynamicExcelColumn("Column1")
|
||||
{
|
||||
Name = "Name of something",
|
||||
Index = 0,
|
||||
Width = 150
|
||||
},
|
||||
new DynamicExcelColumn("Column2")
|
||||
{
|
||||
Name = "Its value",
|
||||
Index = 1,
|
||||
Width = 150
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MiniExcel.SaveAs(path, table, configuration: configuration);
|
||||
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
var rows = stream.Query(useHeaderRow: true)
|
||||
.Select(x => (IDictionary<string, object>)x)
|
||||
.Select(x => (IDictionary<string, object>)x)
|
||||
.ToList();
|
||||
|
||||
Assert.Contains("Name of something", rows[0]);
|
||||
Assert.Contains("Its value", rows[0]);
|
||||
Assert.Contains("Name of something", rows[1]);
|
||||
Assert.Contains("Its value", rows[1]);
|
||||
|
||||
|
||||
Assert.Equal("MiniExcel", rows[0]["Name of something"]);
|
||||
Assert.Equal(1D, rows[0]["Its value"]);
|
||||
Assert.Equal("Github", rows[1]["Name of something"]);
|
||||
|
Loading…
Reference in New Issue
Block a user