From fbfca6354e4f428fce7ffc1115e08a610910dfd8 Mon Sep 17 00:00:00 2001 From: ITWeiHan Date: Sun, 18 Apr 2021 11:59:34 +0800 Subject: [PATCH] - [New] SaveAsByTemplate support input `IEnmerable> or DapperRows` parameters [#201](https://github.com/shps951023/MiniExcel/issues/201) --- docs/README.md | 1 + docs/README.zh-CN.md | 1 + docs/README.zh-Hant.md | 1 + .../OpenXml/ExcelOpenXmlTemplate.Impl.cs | 184 +++++++++++++++++- src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs | 117 +---------- tests/MiniExcelTests/MiniExcelOpenXmlTests.cs | 13 +- .../MiniExcelTests/MiniExcelTemplateTests.cs | 143 +++++++++++++- tests/MiniExcelTests/Utils/Db.cs | 16 ++ tests/MiniExcelTests/Utils/Helpers.cs | 1 + 9 files changed, 348 insertions(+), 129 deletions(-) create mode 100644 tests/MiniExcelTests/Utils/Db.cs diff --git a/docs/README.md b/docs/README.md index 2a8dafa..b221163 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,6 +8,7 @@ ### 0.13.1 - [New] SaveAsByTemplate by template bytes, convenient to cache and support multiple users to read the same template at the same time #189 +- [New] SaveAsByTemplate support input `IEnmerable> or DapperRows` parameters [#201](https://github.com/shps951023/MiniExcel/issues/201) - [Bug] Fix after stream SaveAs/SaveAsByTemplate, miniexcel will close stream [#200](https://github.com/shps951023/MiniExcel/issues/200) ### 0.13.0 diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index 739235d..5f16cef 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -9,6 +9,7 @@ ### 0.13.1 - [New] SaveAsByTemplate 支持读取模板 byte[],方便缓存跟支持多用户同时读取同一个模板 #189 +- [New] SaveAsByTemplate 支持传入 `IEnmerable> 或 DapperRows` 参数 [#201](https://github.com/shps951023/MiniExcel/issues/201) - [Bug] 修正使用 stream SaveAs/SaveAsByTemplate 系统会自动关闭流 stream [#200](https://github.com/shps951023/MiniExcel/issues/200) ### 0.13.0 diff --git a/docs/README.zh-Hant.md b/docs/README.zh-Hant.md index e6ed653..d202369 100644 --- a/docs/README.zh-Hant.md +++ b/docs/README.zh-Hant.md @@ -9,6 +9,7 @@ ### 0.13.1 - [New] SaveAsByTemplate 支持讀取模板 byte[],方便緩存跟支持多用戶同時讀取同一個模板 [#189](https://github.com/shps951023/MiniExcel/issues/189) +- [New] SaveAsByTemplate 支持傳入 `IEnmerable> 或 DapperRows` 參數 [#201](https://github.com/shps951023/MiniExcel/issues/201) - [Bug] 修正使用 stream SaveAs/SaveAsByTemplate 系統會自動關閉流 stream [#200](https://github.com/shps951023/MiniExcel/issues/200) ### 0.13.0 diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs index 6b20d83..6824c8b 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs @@ -21,12 +21,178 @@ namespace MiniExcelLibs.OpenXml public string IEnumerablePropName { get; set; } public XmlElement Row { get; set; } public Type IEnumerableGenricType { get; set; } - public IDictionary PropsMap { get; set; } + public IDictionary PropsMap { get; set; } + public bool IsDictionary { get; set; } public IEnumerable CellIEnumerableValues { get; set; } } + internal class PropInfo + { + public PropertyInfo PropertyInfo { get; set; } + public Type UnderlyingTypePropType { get; set; } + } + private List XRowInfos { get; set; } + private void GenerateSheetXmlImpl(ZipArchiveEntry sheetZipEntry, Stream stream, Stream sheetStream, Dictionary inputMaps, List sharedStrings, XmlWriterSettings xmlWriterSettings = null) + { + var doc = new XmlDocument(); + doc.Load(sheetStream); + sheetStream.Dispose(); + + sheetZipEntry.Delete(); // ZipArchiveEntry can't update directly, so need to delete then create logic + + var worksheet = doc.SelectSingleNode("/x:worksheet", _ns); + var sheetData = doc.SelectSingleNode("/x:worksheet/x:sheetData", _ns); + + var newSheetData = sheetData.Clone(); //avoid delete lost data + var rows = newSheetData.SelectNodes($"x:row", _ns); + + ReplaceSharedStringsToStr(sharedStrings, ref rows); + + //Update dimension && Check if the column contains a collection and get type and properties infomations + UpdateDimensionAndGetCollectionPropertiesInfos(inputMaps, ref doc, ref rows); + + #region Render cell values + + //Q.Why so complex? + //A.Because try to use string stream avoid OOM when rendering rows + sheetData.RemoveAll(); + sheetData.InnerText = "{{{{{{split}}}}}}"; //TODO: bad smell + var prefix = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $"{sheetData.Prefix}:"; + var endPrefix = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $":{sheetData.Prefix}"; //![image](https://user-images.githubusercontent.com/12729184/115000066-fd02b300-9ed4-11eb-8e65-bf0014015134.png) + var contents = doc.InnerXml.Split(new string[] { $"<{prefix}sheetData>{{{{{{{{{{{{split}}}}}}}}}}}}" }, StringSplitOptions.None); ; + using (var writer = new StreamWriter(stream, Encoding.UTF8)) + { + writer.Write(contents[0]); + writer.Write($"<{prefix}sheetData>"); // prefix problem + int originRowIndex; + int rowIndexDiff = 0; + foreach (var xInfo in XRowInfos) + { + var row = xInfo.Row; + + //TODO: some xlsx without r + originRowIndex = int.Parse(row.GetAttribute("r")); + var newRowIndex = originRowIndex + rowIndexDiff; + if (xInfo.CellIEnumerableValues != null) + { + var first = true; + + + + foreach (var item in xInfo.CellIEnumerableValues) + { + var newRow = row.Clone() as XmlElement; + newRow.SetAttribute("r", newRowIndex.ToString()); + newRow.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString()); + + if (xInfo.IsDictionary) + { + var dic = item as IDictionary; + foreach (var propInfo in xInfo.PropsMap) + { + var key = $"{{{{{xInfo.IEnumerablePropName}.{propInfo.Key}}}}}"; + if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png) + { + newRow.InnerXml = newRow.InnerXml.Replace(key, ""); + continue; + } + + var cellValue = dic[propInfo.Key]; + if (cellValue == null) + { + newRow.InnerXml = newRow.InnerXml.Replace(key, ""); + continue; + } + + + var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue); + var type = propInfo.Value.UnderlyingTypePropType; + if (type == typeof(bool)) + { + cellValueStr = (bool)cellValue ? "1" : "0"; + } + else if (type == typeof(DateTime)) + { + //c.SetAttribute("t", "d"); + cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss"); + } + + //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png) + newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr); + } + } + else + { + foreach (var propInfo in xInfo.PropsMap) + { + var prop = propInfo.Value.PropertyInfo; + + var key = $"{{{{{xInfo.IEnumerablePropName}.{prop.Name}}}}}"; + if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png) + { + newRow.InnerXml = newRow.InnerXml.Replace(key, ""); + continue; + } + + var cellValue = prop.GetValue(item); + if (cellValue == null) + { + newRow.InnerXml = newRow.InnerXml.Replace(key, ""); + continue; + } + + + var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue); + var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; + if (type == typeof(bool)) + { + cellValueStr = (bool)cellValue ? "1" : "0"; + } + else if (type == typeof(DateTime)) + { + //c.SetAttribute("t", "d"); + cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss"); + } + + //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png) + newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr); + } + } + + + // note: only first time need add diff ![image](https://user-images.githubusercontent.com/12729184/114494728-6bceda80-9c4f-11eb-9685-8b5ed054eabe.png) + if (!first) + rowIndexDiff++; + first = false; + + newRowIndex++; + writer.Write(CleanXml(newRow.OuterXml, endPrefix)); + newRow = null; + } + } + else + { + row.SetAttribute("r", newRowIndex.ToString()); + row.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString()); + writer.Write(CleanXml(row.OuterXml, endPrefix)); + } + } + writer.Write($""); + writer.Write(contents[1]); + } + #endregion + } + + private static string CleanXml(string xml, string endPrefix) + { + //TODO: need to optimize + return xml + .Replace("xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\"", "") + .Replace($"xmlns{endPrefix}=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"", ""); + } + private void ReplaceSharedStringsToStr(List sharedStrings, ref XmlNodeList rows) { foreach (XmlElement row in rows) @@ -109,7 +275,19 @@ namespace MiniExcelLibs.OpenXml { xRowInfo.IEnumerablePropName = propNames[0]; xRowInfo.IEnumerableGenricType = element.GetType(); - xRowInfo.PropsMap = xRowInfo.IEnumerableGenricType.GetProperties().ToDictionary(s => s.Name, s => s); + if (element is IDictionary) + { + xRowInfo.IsDictionary = true; + var dic = element as IDictionary; + xRowInfo.PropsMap = dic.Keys.ToDictionary(key => key, key => dic[key] != null + ? new PropInfo { UnderlyingTypePropType= Nullable.GetUnderlyingType(dic[key].GetType()) ?? dic[key].GetType() } + : new PropInfo { UnderlyingTypePropType = typeof(object) } ) ; + } + else + { + xRowInfo.PropsMap = xRowInfo.IEnumerableGenricType.GetProperties() + .ToDictionary(s => s.Name, s => new PropInfo { PropertyInfo = s, UnderlyingTypePropType = Nullable.GetUnderlyingType(s.PropertyType) ?? s.PropertyType }); + } } // ==== get demension max rowindex ==== if (!first) //avoid duplicate add first one, this row not add status ![image](https://user-images.githubusercontent.com/12729184/114851829-d2512580-9e14-11eb-8e7d-520c89a7ebee.png) @@ -124,7 +302,7 @@ namespace MiniExcelLibs.OpenXml // auto check type https://github.com/shps951023/MiniExcel/issues/177 var prop = xRowInfo.PropsMap[propNames[1]]; - var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; //avoid nullable + var type = prop.UnderlyingTypePropType; //avoid nullable // if (!xRowInfo.PropsMap.ContainsKey(propNames[1])) throw new InvalidDataException($"{propNames[0]} doesn't have {propNames[1]} property"); diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs index 3de088a..866694c 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs @@ -6,6 +6,7 @@ namespace MiniExcelLibs.OpenXml using System; using System.Collections; using System.Collections.Generic; + using System.Data; using System.IO; using System.IO.Compression; using System.Linq; @@ -93,121 +94,5 @@ namespace MiniExcelLibs.OpenXml _archive.ZipFile.Dispose(); } } - - private void GenerateSheetXmlImpl(ZipArchiveEntry sheetZipEntry, Stream stream, Stream sheetStream, Dictionary inputMaps, List sharedStrings, XmlWriterSettings xmlWriterSettings = null) - { - var doc = new XmlDocument(); - doc.Load(sheetStream); - sheetStream.Dispose(); - - sheetZipEntry.Delete(); // ZipArchiveEntry can't update directly, so need to delete then create logic - - var worksheet = doc.SelectSingleNode("/x:worksheet", _ns); - var sheetData = doc.SelectSingleNode("/x:worksheet/x:sheetData", _ns); - - var newSheetData = sheetData.Clone(); //avoid delete lost data - var rows = newSheetData.SelectNodes($"x:row", _ns); - - ReplaceSharedStringsToStr(sharedStrings, ref rows); - - //Update dimension && Check if the column contains a collection and get type and properties infomations - UpdateDimensionAndGetCollectionPropertiesInfos(inputMaps, ref doc, ref rows); - - #region Render cell values - - //Q.Why so complex? - //A.Because try to use string stream avoid OOM when rendering rows - sheetData.RemoveAll(); - sheetData.InnerText = "{{{{{{split}}}}}}"; //TODO: bad smell - var prefix = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $"{sheetData.Prefix}:"; - var endPrefix = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $":{sheetData.Prefix}"; //![image](https://user-images.githubusercontent.com/12729184/115000066-fd02b300-9ed4-11eb-8e65-bf0014015134.png) - var contents = doc.InnerXml.Split(new string[] { $"<{prefix}sheetData>{{{{{{{{{{{{split}}}}}}}}}}}}" }, StringSplitOptions.None); ; - using (var writer = new StreamWriter(stream, Encoding.UTF8)) - { - writer.Write(contents[0]); - writer.Write($"<{prefix}sheetData>"); // prefix problem - int originRowIndex; - int rowIndexDiff = 0; - foreach (var xInfo in XRowInfos) - { - var row = xInfo.Row; - - //TODO: some xlsx without r - originRowIndex = int.Parse(row.GetAttribute("r")); - var newRowIndex = originRowIndex + rowIndexDiff; - if (xInfo.CellIEnumerableValues != null) - { - var first = true; - foreach (var item in xInfo.CellIEnumerableValues) - { - var newRow = row.Clone() as XmlElement; - newRow.SetAttribute("r", newRowIndex.ToString()); - newRow.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString()); - - foreach (var propInfo in xInfo.PropsMap) - { - var prop = propInfo.Value; - - var key = $"{{{{{xInfo.IEnumerablePropName}.{prop.Name}}}}}"; - if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png) - { - newRow.InnerXml = newRow.InnerXml.Replace(key, ""); - continue; - } - - var cellValue = prop.GetValue(item); - if (cellValue == null) - { - newRow.InnerXml = newRow.InnerXml.Replace(key, ""); - continue; - } - - - var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue); - var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; - if (type == typeof(bool)) - { - cellValueStr = (bool)cellValue ? "1" : "0"; - } - else if (type == typeof(DateTime)) - { - //c.SetAttribute("t", "d"); - cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss"); - } - - //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png) - newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr); - } - - // note: only first time need add diff ![image](https://user-images.githubusercontent.com/12729184/114494728-6bceda80-9c4f-11eb-9685-8b5ed054eabe.png) - if (!first) - rowIndexDiff++; - first = false; - - newRowIndex++; - writer.Write(CleanXml(newRow.OuterXml, endPrefix)); - newRow = null; - } - } - else - { - row.SetAttribute("r", newRowIndex.ToString()); - row.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString()); - writer.Write(CleanXml(row.OuterXml, endPrefix)); - } - } - writer.Write($""); - writer.Write(contents[1]); - } - #endregion - } - - private static string CleanXml(string xml,string endPrefix) - { - //TODO: need to optimize - return xml - .Replace("xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\"", "") - .Replace($"xmlns{endPrefix}=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"", ""); - } } } diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs index 1661f37..cd3e19c 100644 --- a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs +++ b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs @@ -737,7 +737,7 @@ namespace MiniExcelLibs.Tests { { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); - using (var connection = GetConnection("Data Source=:memory:")) + using (var connection = Db.GetConnection("Data Source=:memory:")) { var rows = connection.Query(@"with cte as (select 1 id,2 val) select * from cte where 1=2"); MiniExcel.SaveAs(path, rows); @@ -816,7 +816,7 @@ namespace MiniExcelLibs.Tests // Dapper Query - using (var connection = GetConnection("Data Source=:memory:")) + using (var connection = Db.GetConnection("Data Source=:memory:")) { var rows = connection.Query(@"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2"); MiniExcel.SaveAs(path, rows); @@ -837,7 +837,7 @@ namespace MiniExcelLibs.Tests File.Delete(path); // Empty - using (var connection = GetConnection("Data Source=:memory:")) + using (var connection = Db.GetConnection("Data Source=:memory:")) { var rows = connection.Query(@"with cte as (select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2)select * from cte where 1=2").ToList(); MiniExcel.SaveAs(path, rows); @@ -860,7 +860,7 @@ namespace MiniExcelLibs.Tests // ToList - using (var connection = GetConnection("Data Source=:memory:")) + using (var connection = Db.GetConnection("Data Source=:memory:")) { var rows = connection.Query(@"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2").ToList(); MiniExcel.SaveAs(path, rows); @@ -950,11 +950,6 @@ namespace MiniExcelLibs.Tests File.Delete(path); } - private static SQLiteConnection GetConnection(string connectionString) - { - return new SQLiteConnection(connectionString); - } - [Fact()] public void SQLiteInsertTest() { diff --git a/tests/MiniExcelTests/MiniExcelTemplateTests.cs b/tests/MiniExcelTests/MiniExcelTemplateTests.cs index e3f3a24..ec025be 100644 --- a/tests/MiniExcelTests/MiniExcelTemplateTests.cs +++ b/tests/MiniExcelTests/MiniExcelTemplateTests.cs @@ -1,4 +1,5 @@ -using MiniExcelLibs; +using Dapper; +using MiniExcelLibs; using MiniExcelLibs.Tests.Utils; using System; using System.Collections.Generic; @@ -10,6 +11,146 @@ namespace MiniExcelTests { public class MiniExcelTemplateTests { + [Fact] + public void DapperTemplateTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"..\..\..\..\..\samples\xlsx\TestTemplateComplex.xlsx"; + + var connection = Db.GetConnection("Data Source=:memory:"); + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = connection.Query("select 'Jack' name,'HR' department union all select 'Loan','IT'"), + ["employees"] = connection.Query(@"select 'Wade' name,'HR' department union all select 'Felix','HR' union all select 'Eric','IT' union all select 'Keaton','IT'") + }; + MiniExcel.SaveAsByTemplate(path, templatePath, value); + + { + var rows = MiniExcel.Query(path).ToList(); + + Assert.Equal(9, rows.Count); + + Assert.Equal("FooCompany", rows[0].A); + Assert.Equal("Jack", rows[2].B); + Assert.Equal("HR", rows[2].C); + Assert.Equal("Loan", rows[3].B); + Assert.Equal("IT", rows[3].C); + + Assert.Equal("Wade", rows[5].B); + Assert.Equal("HR", rows[5].C); + Assert.Equal("Felix", rows[6].B); + Assert.Equal("HR", rows[6].C); + + Assert.Equal("Eric", rows[7].B); + Assert.Equal("IT", rows[7].C); + Assert.Equal("Keaton", rows[8].B); + Assert.Equal("IT", rows[8].C); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C9", demension); + } + + { + var rows = MiniExcel.Query(path, sheetName: "Sheet2").ToList(); + + Assert.Equal(9, rows.Count); + + Assert.Equal("FooCompany", rows[0].A); + Assert.Equal("Jack", rows[2].B); + Assert.Equal("HR", rows[2].C); + Assert.Equal("Loan", rows[3].B); + Assert.Equal("IT", rows[3].C); + + Assert.Equal("Wade", rows[5].B); + Assert.Equal("HR", rows[5].C); + Assert.Equal("Felix", rows[6].B); + Assert.Equal("HR", rows[6].C); + + Assert.Equal("Eric", rows[7].B); + Assert.Equal("IT", rows[7].C); + Assert.Equal("Keaton", rows[8].B); + Assert.Equal("IT", rows[8].C); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C9", demension); + } + } + + + [Fact] + public void DictionaryTemplateTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"..\..\..\..\..\samples\xlsx\TestTemplateComplex.xlsx"; + + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = new[] { + new Dictionary{["name"]="Jack",["department"]="HR"}, + new Dictionary{["name"]="Loan",["department"]="IT"} + }, + ["employees"] = new[] { + new Dictionary{["name"]="Wade",["department"]="HR"}, + new Dictionary{["name"]="Felix",["department"]="HR"}, + new Dictionary{["name"]="Eric",["department"]="IT"}, + new Dictionary{["name"]="Keaton",["department"]="IT"} + } + }; + MiniExcel.SaveAsByTemplate(path, templatePath, value); + + { + var rows = MiniExcel.Query(path).ToList(); + + Assert.Equal(9, rows.Count); + + Assert.Equal("FooCompany", rows[0].A); + Assert.Equal("Jack", rows[2].B); + Assert.Equal("HR", rows[2].C); + Assert.Equal("Loan", rows[3].B); + Assert.Equal("IT", rows[3].C); + + Assert.Equal("Wade", rows[5].B); + Assert.Equal("HR", rows[5].C); + Assert.Equal("Felix", rows[6].B); + Assert.Equal("HR", rows[6].C); + + Assert.Equal("Eric", rows[7].B); + Assert.Equal("IT", rows[7].C); + Assert.Equal("Keaton", rows[8].B); + Assert.Equal("IT", rows[8].C); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C9", demension); + } + + { + var rows = MiniExcel.Query(path, sheetName: "Sheet2").ToList(); + + Assert.Equal(9, rows.Count); + + Assert.Equal("FooCompany", rows[0].A); + Assert.Equal("Jack", rows[2].B); + Assert.Equal("HR", rows[2].C); + Assert.Equal("Loan", rows[3].B); + Assert.Equal("IT", rows[3].C); + + Assert.Equal("Wade", rows[5].B); + Assert.Equal("HR", rows[5].C); + Assert.Equal("Felix", rows[6].B); + Assert.Equal("HR", rows[6].C); + + Assert.Equal("Eric", rows[7].B); + Assert.Equal("IT", rows[7].C); + Assert.Equal("Keaton", rows[8].B); + Assert.Equal("IT", rows[8].C); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C9", demension); + } + } + [Fact] public void TestGithubProject() { diff --git a/tests/MiniExcelTests/Utils/Db.cs b/tests/MiniExcelTests/Utils/Db.cs new file mode 100644 index 0000000..1648b70 --- /dev/null +++ b/tests/MiniExcelTests/Utils/Db.cs @@ -0,0 +1,16 @@ +/** + This Class Modified from ExcelDataReader : https://github.com/ExcelDataReader/ExcelDataReader + **/ +namespace MiniExcelLibs.Tests.Utils +{ + using System.Data.SQLite; + + internal static class Db + { + internal static SQLiteConnection GetConnection(string connectionString) + { + return new SQLiteConnection(connectionString); + } + } + +} diff --git a/tests/MiniExcelTests/Utils/Helpers.cs b/tests/MiniExcelTests/Utils/Helpers.cs index 559a05d..8bbf6f3 100644 --- a/tests/MiniExcelTests/Utils/Helpers.cs +++ b/tests/MiniExcelTests/Utils/Helpers.cs @@ -6,6 +6,7 @@ namespace MiniExcelLibs.Tests.Utils using MiniExcelLibs.OpenXml; using System; using System.Collections.Generic; + using System.Data.SQLite; using System.Dynamic; using System.Globalization; using System.IO;