diff --git a/benchmarks/MiniExcel.Benchmarks/Program.cs b/benchmarks/MiniExcel.Benchmarks/Program.cs index 0bebcf0..ed6bb51 100644 --- a/benchmarks/MiniExcel.Benchmarks/Program.cs +++ b/benchmarks/MiniExcel.Benchmarks/Program.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Diagnosers; @@ -84,6 +85,20 @@ namespace MiniExcelLibs.Benchmarks } } + [Benchmark(Description = "MiniExcel Async Template Generate")] + public async Task MiniExcel_Template_Generate_Async_Test() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + const string templatePath = @"TestTemplateBasicIEmumerableFill.xlsx"; + var value = new + { + employees = Enumerable.Range(1, runCount).Select(s => new { name = "Jack", department = "HR" }) + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + } + } + [Benchmark(Description = "ClosedXml.Report Template Generate")] public void ClosedXml_Report_Template_Generate_Test() { @@ -236,6 +251,16 @@ namespace MiniExcelLibs.Benchmarks File.Delete(path); } + [Benchmark(Description = "MiniExcel Async Create Xlsx")] + public async Task MiniExcelCreateAsyncTest() + { + var values = GetValues(); + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + using (var stream = File.Create(path)) + await stream.SaveAsAsync(values); + File.Delete(path); + } + [Benchmark(Description = "ClosedXml Create Xlsx")] public void ClosedXmlCreateTest() { @@ -275,7 +300,7 @@ namespace MiniExcelLibs.Benchmarks var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); // Create a spreadsheet document by supplying the filepath. // By default, AutoSave = true, Editable = true, and Type = xlsx. - + using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook)) { // Add a WorkbookPart to the document. diff --git a/src/MiniExcel/Csv/CsvReader.cs b/src/MiniExcel/Csv/CsvReader.cs index c0b139f..2f5b545 100644 --- a/src/MiniExcel/Csv/CsvReader.cs +++ b/src/MiniExcel/Csv/CsvReader.cs @@ -4,11 +4,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; using static MiniExcelLibs.Utils.Helpers; namespace MiniExcelLibs.Csv { - internal class CsvReader : IExcelReader + internal class CsvReader : IExcelReader , IExcelReaderAsync { private Stream _stream; public CsvReader(Stream stream) @@ -127,5 +128,15 @@ namespace MiniExcelLibs.Csv .Select(s => Regex.Replace(s.Replace("\"\"", "\""), "^\"|\"$", "")).ToArray(); //this code from S.O : https://stackoverflow.com/a/11365961/9131476 } + + public Task>> QueryAsync(bool UseHeaderRow, string sheetName, string startCell, IConfiguration configuration) + { + return Task.Run(() => Query(UseHeaderRow, sheetName, startCell, configuration)); + } + + public Task> QueryAsync(string sheetName, string startCell, IConfiguration configuration) where T : class, new() + { + return Task.Run(() => Query(sheetName, startCell, configuration)); + } } } diff --git a/src/MiniExcel/Csv/CsvWriter.cs b/src/MiniExcel/Csv/CsvWriter.cs index 3221ad6..2498be5 100644 --- a/src/MiniExcel/Csv/CsvWriter.cs +++ b/src/MiniExcel/Csv/CsvWriter.cs @@ -6,11 +6,12 @@ using System.Data; using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using static MiniExcelLibs.Utils.Helpers; namespace MiniExcelLibs.Csv { - internal class CsvWriter : IExcelWriter + internal class CsvWriter : IExcelWriter , IExcelWriterAsync { private Stream _stream; @@ -82,7 +83,7 @@ namespace MiniExcelLibs.Csv //if(mode == null) // throw new NotImplementedException($"Type {type?.Name} & genericType {genericType?.Name} not Implemented. please issue for me."); - if (keys.Count() == 0 && props == null) + if (keys.Count == 0 && props == null) { writer.Write(newLine); return; @@ -211,6 +212,12 @@ namespace MiniExcelLibs.Csv writer.Write(newLine); } } + + public Task SaveAsAsync(object value, string sheetName, bool printHeader, IConfiguration configuration) + { + return Task.Run(() => SaveAs(value, sheetName, printHeader, configuration)); + } + } internal static class CsvValueTostringHelper diff --git a/src/MiniExcel/ExcelFacorty.cs b/src/MiniExcel/ExcelFacorty.cs index 7cfd0ca..1eee280 100644 --- a/src/MiniExcel/ExcelFacorty.cs +++ b/src/MiniExcel/ExcelFacorty.cs @@ -7,7 +7,7 @@ internal class ExcelWriterFactory { - internal static IExcelWriter GetProvider(Stream stream,ExcelType excelType) + internal static IExcelWriterAsync GetProvider(Stream stream,ExcelType excelType) { switch (excelType) { @@ -23,7 +23,7 @@ internal class ExcelReaderFactory { - internal static IExcelReader GetProvider(Stream stream, ExcelType excelType) + internal static IExcelReaderAsync GetProvider(Stream stream, ExcelType excelType) { switch (excelType) { @@ -39,7 +39,7 @@ internal class ExcelTemplateFactory { - internal static IExcelTemplate GetProvider(Stream stream, ExcelType excelType= ExcelType.XLSX) + internal static IExcelTemplateAsync GetProvider(Stream stream, ExcelType excelType= ExcelType.XLSX) { switch (excelType) { diff --git a/src/MiniExcel/IExcelReader.cs b/src/MiniExcel/IExcelReader.cs index 525fd40..1af66cf 100644 --- a/src/MiniExcel/IExcelReader.cs +++ b/src/MiniExcel/IExcelReader.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; namespace MiniExcelLibs { @@ -7,4 +8,10 @@ namespace MiniExcelLibs IEnumerable> Query(bool UseHeaderRow, string sheetName,string startCell, IConfiguration configuration); IEnumerable Query(string sheetName, string startCell, IConfiguration configuration) where T : class, new(); } + + internal interface IExcelReaderAsync : IExcelReader + { + Task>> QueryAsync(bool UseHeaderRow, string sheetName, string startCell, IConfiguration configuration); + Task> QueryAsync(string sheetName, string startCell, IConfiguration configuration) where T : class, new(); + } } diff --git a/src/MiniExcel/IExcelTemplate.cs b/src/MiniExcel/IExcelTemplate.cs index 53ab251..a653cd6 100644 --- a/src/MiniExcel/IExcelTemplate.cs +++ b/src/MiniExcel/IExcelTemplate.cs @@ -1,8 +1,16 @@ -namespace MiniExcelLibs +using System.Threading.Tasks; + +namespace MiniExcelLibs { internal interface IExcelTemplate { void SaveAsByTemplate(string templatePath, object value); void SaveAsByTemplate(byte[] templateBtyes, object value); } + + internal interface IExcelTemplateAsync : IExcelTemplate + { + Task SaveAsByTemplateAsync(string templatePath, object value); + Task SaveAsByTemplateAsync(byte[] templateBtyes, object value); + } } diff --git a/src/MiniExcel/IExcelWriter.cs b/src/MiniExcel/IExcelWriter.cs index a2ae072..18c42d2 100644 --- a/src/MiniExcel/IExcelWriter.cs +++ b/src/MiniExcel/IExcelWriter.cs @@ -1,9 +1,15 @@ using System.IO; +using System.Threading.Tasks; namespace MiniExcelLibs { - internal interface IExcelWriter + internal interface IExcelWriter { void SaveAs(object value,string sheetName, bool printHeader, IConfiguration configuration); } + + internal interface IExcelWriterAsync : IExcelWriter + { + Task SaveAsAsync(object value, string sheetName, bool printHeader, IConfiguration configuration); + } } diff --git a/src/MiniExcel/MiniExcel.cs b/src/MiniExcel/MiniExcel.cs index e6f1df0..c2cc193 100644 --- a/src/MiniExcel/MiniExcel.cs +++ b/src/MiniExcel/MiniExcel.cs @@ -8,6 +8,7 @@ using System.Data; using System.IO; using System.Linq; + using System.Threading.Tasks; public static partial class MiniExcel { @@ -19,13 +20,19 @@ SaveAs(stream, value, printHeader, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration); } + public static Task SaveAsAsync(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) + { + return Task.Run(() => SaveAs(path, value, printHeader, sheetName, excelType , configuration)); + } + public static void SaveAs(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) { - if (string.IsNullOrEmpty(sheetName)) - throw new InvalidDataException("Sheet name can not be empty or null"); - if (excelType == ExcelType.UNKNOWN) - throw new InvalidDataException("Please specify excelType"); - ExcelWriterFactory.GetProvider(stream, excelType).SaveAs(value, sheetName, printHeader, configuration); + GetWriterProvider(stream, sheetName, excelType).SaveAs(value, sheetName, printHeader, configuration); + } + + public static Task SaveAsAsync(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + return GetWriterProvider(stream, sheetName, excelType).SaveAsAsync(value, sheetName, printHeader, configuration); } public static IEnumerable Query(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() @@ -40,6 +47,21 @@ return ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)).Query(sheetName, startCell, configuration); } + public static Task> QueryAsync(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + return Task.Run(() => Query(path, useHeaderRow, sheetName, excelType, startCell, configuration)); + } + + public static Task> QueryAsync(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() + { + return ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)).QueryAsync(sheetName, startCell, configuration); + } + + public static Task> QueryAsync(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() + { + return Task.Run(() => Query(path, sheetName, excelType, startCell, configuration)); + } + public static IEnumerable Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) { using (var stream = Helpers.OpenSharedRead(path)) @@ -52,6 +74,11 @@ return ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)).Query(useHeaderRow, sheetName, startCell, configuration); } + public static Task>> QueryAsync(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + return GetReaderProvider(stream, excelType).QueryAsync(useHeaderRow, sheetName, startCell, configuration); + } + public static List GetSheetNames(string path) { using (var stream = Helpers.OpenSharedRead(path)) @@ -97,6 +124,27 @@ ExcelTemplateFactory.GetProvider(stream).SaveAsByTemplate(templateBytes, value); } + public static Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value) + { + return ExcelTemplateFactory.GetProvider(stream).SaveAsByTemplateAsync(templatePath, value); + } + + public static Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value) + { + return ExcelTemplateFactory.GetProvider(stream).SaveAsByTemplateAsync(templateBytes, value); + } + + public static Task SaveAsByTemplateAsync(string path, string templatePath, object value) + { + return Task.Run(() => SaveAsByTemplate(path, templatePath, value)); + } + + public static Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value) + { + return Task.Run(() => SaveAsByTemplate(path, templateBytes, value)); + } + + /// /// QueryAsDataTable is not recommended, because it'll load all data into memory. /// @@ -106,6 +154,10 @@ return QueryAsDataTable(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration); } + public static Task QueryAsDataTableAsync(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + return Task.Run(() => QueryAsDataTable(path, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration)); + } /// /// QueryAsDataTable is not recommended, because it'll load all data into memory. /// @@ -114,5 +166,19 @@ return ExcelOpenXmlSheetReader.QueryAsDataTableImpl(stream, useHeaderRow, ref sheetName, excelType, startCell, configuration); } + private static IExcelWriterAsync GetWriterProvider(Stream stream, string sheetName, ExcelType excelType) + { + if (string.IsNullOrEmpty(sheetName)) + throw new InvalidDataException("Sheet name can not be empty or null"); + if (excelType == ExcelType.UNKNOWN) + throw new InvalidDataException("Please specify excelType"); + + return ExcelWriterFactory.GetProvider(stream, excelType); + } + + private static IExcelReaderAsync GetReaderProvider(Stream stream, ExcelType excelType) + { + return ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType)); + } } } diff --git a/src/MiniExcel/OpenXml/DefualtOpenXml.cs b/src/MiniExcel/OpenXml/DefualtOpenXml.cs index 04f3420..27be88e 100644 --- a/src/MiniExcel/OpenXml/DefualtOpenXml.cs +++ b/src/MiniExcel/OpenXml/DefualtOpenXml.cs @@ -10,7 +10,7 @@ namespace MiniExcelLibs.OpenXml { internal static class DefualtOpenXml { - private readonly static UTF8Encoding Utf8WithBom = new System.Text.UTF8Encoding(true); + private readonly static UTF8Encoding Utf8WithBom = new UTF8Encoding(true); private static string DefaultRels = @" diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs index 0f0de8f..7151d66 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs @@ -8,12 +8,13 @@ using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; +using System.Threading.Tasks; using System.Xml; using static MiniExcelLibs.Utils.Helpers; namespace MiniExcelLibs.OpenXml { - internal class ExcelOpenXmlSheetReader : IExcelReader + internal class ExcelOpenXmlSheetReader : IExcelReader, IExcelReaderAsync { private const string _ns = Config.SpreadsheetmlXmlns; private List _sheetRecords; @@ -706,5 +707,15 @@ namespace MiniExcelLibs.OpenXml return; } } + + public Task>> QueryAsync(bool UseHeaderRow, string sheetName, string startCell, IConfiguration configuration) + { + return Task.Run(() => Query(UseHeaderRow, sheetName, startCell, configuration)); + } + + public Task> QueryAsync(string sheetName, string startCell, IConfiguration configuration) where T : class, new() + { + return Task.Run(() => Query(sheetName, startCell, configuration)); + } } } diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index 1fded17..3acddb6 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -8,6 +8,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Text; +using System.Threading.Tasks; using static MiniExcelLibs.Utils.Helpers; namespace MiniExcelLibs.OpenXml @@ -18,7 +19,7 @@ namespace MiniExcelLibs.OpenXml public object Values { get; set; } } - internal class ExcelOpenXmlSheetWriter : IExcelWriter + internal class ExcelOpenXmlSheetWriter : IExcelWriter , IExcelWriterAsync { private readonly static UTF8Encoding _utf8WithBom = new System.Text.UTF8Encoding(true); private Stream _stream; @@ -480,5 +481,10 @@ namespace MiniExcelLibs.OpenXml dimensionRef = $"A1:{Helpers.GetAlphabetColumnName(maxColumnIndex - 1)}{maxRowIndex}"; return dimensionRef; } + + public Task SaveAsAsync(object value, string sheetName, bool printHeader, IConfiguration configuration) + { + return Task.Run(() => SaveAs(value, sheetName, printHeader, configuration)); + } } } diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs index 36b8cf0..a9df326 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.cs @@ -13,9 +13,10 @@ namespace MiniExcelLibs.OpenXml using System.Reflection; using System.Text; using System.Text.RegularExpressions; + using System.Threading.Tasks; using System.Xml; - internal partial class ExcelOpenXmlTemplate:IExcelTemplate + internal partial class ExcelOpenXmlTemplate:IExcelTemplate,IExcelTemplateAsync { private static readonly XmlNamespaceManager _ns; private static readonly Regex _isExpressionRegex; @@ -66,7 +67,7 @@ namespace MiniExcelLibs.OpenXml templateStream.CopyTo(stream); var reader = new ExcelOpenXmlSheetReader(stream); - var _archive = new ExcelOpenXmlZip(stream, mode: ZipArchiveMode.Update, true, Encoding.UTF8); + using (var _archive = new ExcelOpenXmlZip(stream, mode: ZipArchiveMode.Update, true, Encoding.UTF8)) { //read sharedString var sharedStrings = reader.GetSharedStrings(); @@ -93,9 +94,17 @@ namespace MiniExcelLibs.OpenXml } } } - - _archive.ZipFile.Dispose(); } } + + public Task SaveAsByTemplateAsync(string templatePath, object value) + { + return Task.Run(() => SaveAsByTemplate(templatePath, value)); + } + + public Task SaveAsByTemplateAsync(byte[] templateBtyes, object value) + { + return Task.Run(() => SaveAsByTemplate(templateBtyes, value)); + } } } diff --git a/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs b/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs new file mode 100644 index 0000000..a88cd17 --- /dev/null +++ b/tests/MiniExcelTests/MiniExcelCsvAsycTests.cs @@ -0,0 +1,273 @@ +using CsvHelper; +using MiniExcelLibs.Tests.Utils; +using System; +using System.Collections.Generic; +using System.Data; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MiniExcelLibs.Tests +{ + public class MiniExcelCsvAsycTests + { + [Fact] + public async Task gb2312_Encoding_Read_Test() + { + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + var path = PathHelper.GetSamplePath("csv/gb2312_Encoding_Read_Test.csv"); + var config = new MiniExcelLibs.Csv.CsvConfiguration() + { + StreamReaderFunc = (stream) => new StreamReader(stream, encoding: Encoding.GetEncoding("gb2312")) + }; + var q = await MiniExcel.QueryAsync(path, true, excelType: ExcelType.CSV, configuration: config); + var rows = q.ToList(); + Assert.Equal("世界你好", rows[0].栏位1); + } + + [Fact] + public async Task SeperatorTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var values = new List>() + { + new Dictionary{{ "a", @"""<>+-*//}{\\n" }, { "b", 1234567890 },{ "c", true },{ "d", new DateTime(2021, 1, 1) } }, + new Dictionary{{ "a", @"Hello World" }, { "b", -1234567890 },{ "c", false },{ "d", new DateTime(2021, 1, 2) } }, + }; + await MiniExcel.SaveAsAsync(path, values, configuration: new MiniExcelLibs.Csv.CsvConfiguration() { Seperator = ';' }); + var expected = @"a;b;c;d +""""""<>+-*//}{\\n"";1234567890;True;""2021-01-01 00:00:00"" +""Hello World"";-1234567890;False;""2021-01-02 00:00:00"" +"; + Assert.Equal(expected, File.ReadAllText(path)); + } + + [Fact] + public async Task SaveAsByDictionary() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var table = new List>(); + await MiniExcel.SaveAsAsync(path, table); + Assert.Equal("\r\n", File.ReadAllText(path)); + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var table = new Dictionary(); //TODO + await MiniExcel.SaveAsAsync(path, table); + //Assert.Throws(() => MiniExcel.SaveAs(path, table)); + Assert.Equal("\r\n", File.ReadAllText(path)); + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var values = new List>() + { + new Dictionary{{ "a", @"""<>+-*//}{\\n" }, { "b", 1234567890 },{ "c", true },{ "d", new DateTime(2021, 1, 1) } }, + new Dictionary{{ "a", @"Hello World" }, { "b", -1234567890 },{ "c", false },{ "d", new DateTime(2021, 1, 2) } }, + }; + await MiniExcel.SaveAsAsync(path, values); + + using (var reader = new StreamReader(path)) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var records = csv.GetRecords().ToList(); + Assert.Equal(@"""<>+-*//}{\\n", records[0].a); + Assert.Equal(@"1234567890", records[0].b); + Assert.Equal(@"True", records[0].c); + Assert.Equal(@"2021-01-01 00:00:00", records[0].d); + + Assert.Equal(@"Hello World", records[1].a); + Assert.Equal(@"-1234567890", records[1].b); + Assert.Equal(@"False", records[1].c); + Assert.Equal(@"2021-01-02 00:00:00", records[1].d); + } + + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var values = new List>() + { + new Dictionary{{ 1, @"""<>+-*//}{\\n" }, { 2, 1234567890 },{ 3, true },{ 4, new DateTime(2021, 1, 1) } }, + new Dictionary{{ 1, @"Hello World" }, { 2, -1234567890 },{ 3, false },{4, new DateTime(2021, 1, 2) } }, + }; + await MiniExcel.SaveAsAsync(path, values); + + using (var reader = new StreamReader(path)) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var records = csv.GetRecords().ToList(); + { + var row = records[0] as IDictionary; + Assert.Equal(@"""<>+-*//}{\\n", row["1"]); + Assert.Equal(@"1234567890", row["2"]); + Assert.Equal(@"True", row["3"]); + Assert.Equal(@"2021-01-01 00:00:00", row["4"]); + } + { + var row = records[1] as IDictionary; + Assert.Equal(@"Hello World", row["1"]); + Assert.Equal(@"-1234567890", row["2"]); + Assert.Equal(@"False", row["3"]); + Assert.Equal(@"2021-01-02 00:00:00", row["4"]); + } + } + + File.Delete(path); + } + } + + [Fact] + public async Task SaveAsByDataTableTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + var table = new DataTable(); + await MiniExcel.SaveAsAsync(path, table); + + var text = File.ReadAllText(path); + Assert.Equal("\r\n", text); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + + var table = new DataTable(); + { + table.Columns.Add("a", typeof(string)); + table.Columns.Add("b", typeof(decimal)); + table.Columns.Add("c", typeof(bool)); + table.Columns.Add("d", typeof(DateTime)); + table.Rows.Add(@"""<>+-*//}{\\n", 1234567890, true, new DateTime(2021, 1, 1)); + table.Rows.Add(@"Hello World", -1234567890, false, new DateTime(2021, 1, 2)); + } + + await MiniExcel.SaveAsAsync(path, table); + + using (var reader = new StreamReader(path)) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var records = csv.GetRecords().ToList(); + Assert.Equal(@"""<>+-*//}{\\n", records[0].a); + Assert.Equal(@"1234567890", records[0].b); + Assert.Equal(@"True", records[0].c); + Assert.Equal(@"2021-01-01 00:00:00", records[0].d); + + Assert.Equal(@"Hello World", records[1].a); + Assert.Equal(@"-1234567890", records[1].b); + Assert.Equal(@"False", records[1].c); + Assert.Equal(@"2021-01-02 00:00:00", records[1].d); + } + + File.Delete(path); + } + + } + + + public class Test + { + public string c1 { get; set; } + public string c2 { get; set; } + } + + [Fact] + public async Task CsvExcelTypeTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); + var input = new[] { new { A = "Test1", B = "Test2" } }; + await MiniExcel.SaveAsAsync(path, input); + + var texts = File.ReadAllLines(path); + Assert.Equal("A,B", texts[0]); + Assert.Equal("Test1,Test2", texts[1]); + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("A", rows[0].A); + Assert.Equal("B", rows[0].B); + Assert.Equal("Test1", rows[1].A); + Assert.Equal("Test2", rows[1].B); + } + + using (var reader = new StreamReader(path)) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var rows = csv.GetRecords().ToList(); + Assert.Equal("Test1", rows[0].A); + Assert.Equal("Test2", rows[0].B); + } + + File.Delete(path); + } + } + + [Fact()] + public async Task Create2x2_Test() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); + await MiniExcel.SaveAsAsync(path, new[] { + new { c1 = "A1" ,c2 = "B1"}, + new { c1 = "A2" ,c2 = "B2"}, + }); + + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(useHeaderRow: true, excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal("B1", rows[0].c2); + Assert.Equal("A2", rows[1].c1); + Assert.Equal("B2", rows[1].c2); + } + + { + var rows = MiniExcel.Query(path, useHeaderRow: true, excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal("B1", rows[0].c2); + Assert.Equal("A2", rows[1].c1); + Assert.Equal("B2", rows[1].c2); + } + + File.Delete(path); + } + + [Fact()] + public async Task CsvTypeMappingTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); + await MiniExcel.SaveAsAsync(path, new[] { + new { c1 = "A1" ,c2 = "B1"}, + new { c1 = "A2" ,c2 = "B2"}, + }); + + using (var stream = File.OpenRead(path)) + { + var rows = stream.Query(excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal("B1", rows[0].c2); + Assert.Equal("A2", rows[1].c1); + Assert.Equal("B2", rows[1].c2); + } + + { + var rows = MiniExcel.Query(path, excelType: ExcelType.CSV).ToList(); + Assert.Equal("A1", rows[0].c1); + Assert.Equal("B1", rows[0].c2); + Assert.Equal("A2", rows[1].c1); + Assert.Equal("B2", rows[1].c2); + } + + File.Delete(path); + } + } +} \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelIssueAsyncTests.cs b/tests/MiniExcelTests/MiniExcelIssueAsyncTests.cs new file mode 100644 index 0000000..a594890 --- /dev/null +++ b/tests/MiniExcelTests/MiniExcelIssueAsyncTests.cs @@ -0,0 +1,1685 @@ +using Dapper; +using MiniExcelLibs.Attributes; +using MiniExcelLibs.OpenXml; +using MiniExcelLibs.Tests.Utils; +using Newtonsoft.Json; +using OfficeOpenXml; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SQLite; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; +using static MiniExcelLibs.Tests.MiniExcelOpenXmlTests; + +namespace MiniExcelLibs.Tests +{ + public partial class MiniExcelIssueAsyncTests + { + private readonly ITestOutputHelper output; + public MiniExcelIssueAsyncTests(ITestOutputHelper output) + { + this.output = output; + } + + /// + /// [SaveAsByTemplate support DateTime custom format · Issue #255 · shps951023/MiniExcel] + /// (https://github.com/shps951023/MiniExcel/issues/255) + /// + [Fact] + public async Task Issue255() + { + //tempalte + { + var templatePath = PathHelper.GetSamplePath("xlsx/TestsIssue255_Template.xlsx"); + var path = PathHelper.GetTempPath(); + var value = new + { + Issue255DTO = new Issue255DTO[] { + new Issue255DTO { Time = new DateTime(2021, 01, 01) } + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("2021", rows[1].A.ToString()); + } + //saveas + { + var path = PathHelper.GetTempPath(); + var value = new Issue255DTO[] { + new Issue255DTO { Time = new DateTime(2021, 01, 01) } + }; + await MiniExcel.SaveAsAsync(path, value); + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("2021", rows[1].A.ToString()); + } + } + + public class Issue255DTO + { + [ExcelFormat("yyyy")] + public DateTime Time { get; set; } + } + + /// + /// [Dynamic QueryAsync custom format not using mapping format · Issue #256] + /// (https://github.com/shps951023/MiniExcel/issues/256) + /// + [Fact] + public async Task Issue256() + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue256.xlsx"); + var q = await MiniExcel.QueryAsync(path, false); + var rows = q.ToList(); + Assert.Equal(new DateTime(2003, 4, 16), rows[1].A); + Assert.Equal(new DateTime(2004, 4, 16), rows[1].B); + } + + + /// + /// Csv SaveAs by datareader with encoding default show messy code #253 + /// + [Fact] + public async Task Issue253() + { + { + var value = new[] { new { col1 = "世界你好" } }; + var path = PathHelper.GetTempPath(extension: "csv"); + await MiniExcel.SaveAsAsync(path, value); + var expected = @"col1 +世界你好 +"; + Assert.Equal(expected, File.ReadAllText(path)); + } + + { + System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); + var value = new[] { new { col1 = "世界你好" } }; + var path = PathHelper.GetTempPath(extension: "csv"); + var config = new MiniExcelLibs.Csv.CsvConfiguration() + { + StreamWriterFunc = (stream) => new StreamWriter(stream, Encoding.GetEncoding("gb2312")) + }; + await MiniExcel.SaveAsAsync(path, value, excelType: ExcelType.CSV, configuration: config); + var expected = @"col1 +������� +"; + Assert.Equal(expected, File.ReadAllText(path)); + } + + using (var cn = Db.GetConnection()) + { + var value = cn.ExecuteReader(@"select '世界你好' col1"); + var path = PathHelper.GetTempPath(extension: "csv"); + await MiniExcel.SaveAsAsync(path, value); + var expected = @"col1 +世界你好 +"; + Assert.Equal(expected, File.ReadAllText(path)); + } + } + + /// + /// [CSV SaveAs support datareader · Issue #251 · shps951023/MiniExcel](https://github.com/shps951023/MiniExcel/issues/251) + /// + [Fact] + public async Task Issue251() + { + using (var cn = Db.GetConnection()) + { + var reader = await cn.ExecuteReaderAsync(@"select '""<>+-*//}{\\n' a,1234567890 b union all select 'Hello World',-1234567890"); + var path = PathHelper.GetTempPath(extension: "csv"); + await MiniExcel.SaveAsAsync(path, reader); + var expected = @"a,b +""""""<>+-*//}{\\n"",1234567890 +""Hello World"",-1234567890 +"; + Assert.Equal(expected, File.ReadAllText(path)); + } + } + + /// + /// No error exception throw when reading xls file #242 + /// + [Fact] + public async Task Issue242() + { + var path = PathHelper.GetSamplePath("xls/TestIssue242.xls"); + + await Assert.ThrowsAsync(async () => { + var q = await MiniExcel.QueryAsync(path); + q.ToList(); + }); + + using (var stream = File.OpenRead(path)) + { + await Assert.ThrowsAsync(async () => { + var q = await stream.QueryAsync(); + q.ToList(); + }); + } + } + + /// + /// Csv type mapping QueryAsync error "cannot be converted to xxx type" #243 + /// + [Fact] + public async Task Issue243() + { + var path = PathHelper.GetTempPath("csv"); + var value = new[] { + new { name ="Jack",Age=25,InDate=new DateTime(2021,01,03)}, + new { name ="Henry",Age=36,InDate=new DateTime(2020,05,03)}, + }; + await MiniExcel.SaveAsAsync(path, value); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("Jack", rows[0].name); + Assert.Equal(25, rows[0].Age); + Assert.Equal(new DateTime(2021, 01, 03), rows[0].InDate); + + Assert.Equal("Henry", rows[1].name); + Assert.Equal(36, rows[1].Age); + Assert.Equal(new DateTime(2020, 05, 03), rows[1].InDate); + } + + public class Issue243Dto + { + public string name { get; set; } + public int Age { get; set; } + public DateTime InDate { get; set; } + } + + /// + /// Support Custom Datetime format #241 + /// + [Fact] + public async Task Issue241() + { + + var value = new Issue241Dto[] { + new Issue241Dto{ Name="Jack",InDate=new DateTime(2021,01,04)}, + new Issue241Dto{ Name="Henry",InDate=new DateTime(2020,04,05)}, + }; + + // csv + { + var path = PathHelper.GetTempPath("csv"); + MiniExcel.SaveAs(path, value); + + { + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal(rows[0].InDate, "01 04, 2021"); + Assert.Equal(rows[1].InDate, "04 05, 2020"); + } + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(rows[0].InDate, new DateTime(2021, 01, 04)); + Assert.Equal(rows[1].InDate, new DateTime(2020, 04, 05)); + } + } + + // xlsx + { + var path = PathHelper.GetTempPath(); + await MiniExcel.SaveAsAsync(path, value); + + { + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal(rows[0].InDate, "01 04, 2021"); + Assert.Equal(rows[1].InDate, "04 05, 2020"); + } + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(rows[0].InDate, new DateTime(2021, 01, 04)); + Assert.Equal(rows[1].InDate, new DateTime(2020, 04, 05)); + } + } + } + + public class Issue241Dto + { + public string Name { get; set; } + + [ExcelFormat("MM dd, yyyy")] + public DateTime InDate { get; set; } + } + + /// + /// SaveAs Default Template #132 + /// + [Fact] + public async Task Issue132() + { + { + var path = PathHelper.GetTempPath(); + var value = new[] { + new { name ="Jack",Age=25,InDate=new DateTime(2021,01,03)}, + new { name ="Henry",Age=36,InDate=new DateTime(2020,05,03)}, + }; + + await MiniExcel.SaveAsAsync(path, value); + } + + { + var path = PathHelper.GetTempPath(); + var value = new[] { + new { name ="Jack",Age=25,InDate=new DateTime(2021,01,03)}, + new { name ="Henry",Age=36,InDate=new DateTime(2020,05,03)}, + }; + var config = new OpenXmlConfiguration() + { + TableStyles = TableStyles.None + }; + await MiniExcel.SaveAsAsync(path, value, configuration: config); + } + + { + var path = PathHelper.GetTempPath(); + var value = JsonConvert.DeserializeObject( + JsonConvert.SerializeObject(new[] { + new { name ="Jack",Age=25,InDate=new DateTime(2021,01,03)}, + new { name ="Henry",Age=36,InDate=new DateTime(2020,05,03)}, + }) + ); + await MiniExcel.SaveAsAsync(path, value); + } + } + + /// + /// Support SaveAs by DataSet #235 + /// + [Fact] + public async Task Issue235() + { + var path = PathHelper.GetTempPath(); + + DataSet sheets = new DataSet(); + var users = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } })); + users.TableName = "users"; + var department = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } })); ; + department.TableName = "department"; + sheets.Tables.Add(users); + sheets.Tables.Add(department); + + await MiniExcel.SaveAsAsync(path, sheets); + + + var sheetNames = MiniExcel.GetSheetNames(path); + Assert.Equal("users", sheetNames[0]); + Assert.Equal("department", sheetNames[1]); + + { + var q = await MiniExcel.QueryAsync(path, true, sheetName: "users"); + var rows = q.ToList(); + Assert.Equal("Jack", rows[0].Name); + Assert.Equal(25, rows[0].Age); + Assert.Equal("Mike", rows[1].Name); + Assert.Equal(44, rows[1].Age); + } + { + var q = await MiniExcel.QueryAsync(path, true, sheetName: "department"); + var rows = q.ToList(); + Assert.Equal("01", rows[0].ID); + Assert.Equal("HR", rows[0].Name); + Assert.Equal("02", rows[1].ID); + Assert.Equal("IT", rows[1].Name); + } + } + + /// + /// QueryAsDataTable A2=5.5 , A3=0.55/1.1 will case double type check error #233 + /// + [Fact] + public async Task Issue233() + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue233.xlsx"); + var dt = await MiniExcel.QueryAsDataTableAsync(path); + var rows = dt.Rows; + + Assert.Equal(0.55, rows[0]["Size"]); + Assert.Equal("0.55/1.1", rows[1]["Size"]); + } + + /// + /// Csv QueryAsync split comma not correct #237 + /// https://github.com/shps951023/MiniExcel/issues/237 + /// + [Fact] + public async Task Issue237() + { + var value = new[] + { + new{ id="\"\"1,2,3\"\""}, + new{ id="1,2,3"}, + }; + var path = PathHelper.GetTempPath("csv"); + await MiniExcel.SaveAsAsync(path, value); + + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal("\"\"1,2,3\"\"", rows[0].id); + Assert.Equal("1,2,3", rows[1].id); + } + + /// + /// SaveAs support multiple sheets #234 + /// + [Fact] + public async Task Issue234() + { + var path = PathHelper.GetTempPath(); + + 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 + { + ["users"] = users, + ["department"] = department + }; + await MiniExcel.SaveAsAsync(path, sheets); + + var sheetNames = MiniExcel.GetSheetNames(path); + Assert.Equal("users", sheetNames[0]); + Assert.Equal("department", sheetNames[1]); + + { + var q = await MiniExcel.QueryAsync(path, true, sheetName: "users"); + var rows = q.ToList(); + Assert.Equal("Jack", rows[0].Name); + Assert.Equal(25, rows[0].Age); + Assert.Equal("Mike", rows[1].Name); + Assert.Equal(44, rows[1].Age); + } + { + var q = await MiniExcel.QueryAsync(path, true, sheetName: "department"); + var rows = q.ToList(); + Assert.Equal("01", rows[0].ID); + Assert.Equal("HR", rows[0].Name); + Assert.Equal("02", rows[1].ID); + Assert.Equal("IT", rows[1].Name); + } + } + + /// + /// SaveAs By Reader Closed error : 'Error! Invalid attempt to call FieldCount when reader is closed' #230 + /// https://github.com/shps951023/MiniExcel/issues/230 + /// + [Fact] + public async Task Issue230() + { + var conn = Db.GetConnection("Data Source=:memory:"); + conn.Open(); + var cmd = conn.CreateCommand(); + cmd.CommandText = "select 1 id union all select 2"; + using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection)) + { + while (reader.Read()) + { + for (int i = 0; i < reader.FieldCount; i++) + { + var result = $"{reader.GetName(i)} , {reader.GetValue(i)}"; + output.WriteLine(result); + } + } + } + + conn = Db.GetConnection("Data Source=:memory:"); + conn.Open(); + cmd = conn.CreateCommand(); + cmd.CommandText = "select 1 id union all select 2"; + using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection)) + { + while (reader.Read()) + { + for (int i = 0; i < reader.FieldCount; i++) + { + var result = $"{reader.GetName(i)} , {reader.GetValue(i)}"; + output.WriteLine(result); + } + } + } + + conn = Db.GetConnection("Data Source=:memory:"); + conn.Open(); + cmd = conn.CreateCommand(); + cmd.CommandText = "select 1 id union all select 2"; + using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection)) + { + var path = PathHelper.GetTempPath(); + await MiniExcel.SaveAsAsync(path, reader, printHeader: true); + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal(1, rows[0].id); + Assert.Equal(2, rows[1].id); + } + } + + /// + /// v0.14.3 QueryAsDataTable error "Cannot set Column to be null" #229 + /// https://github.com/shps951023/MiniExcel/issues/229 + /// + [Fact] + public async Task Issue229() + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue229.xlsx"); + var dt = await MiniExcel.QueryAsDataTableAsync(path); + foreach (DataColumn column in dt.Columns) + { + var v = dt.Rows[3][column]; + var type = v?.GetType(); + Assert.Equal(DBNull.Value, v); + } + } + + /// + /// [QueryAsync Merge cells data · Issue #122 · shps951023/MiniExcel] + /// (https://github.com/shps951023/MiniExcel/issues/122) + /// + [Fact] + public async Task Issue122() + { + var config = new OpenXmlConfiguration() + { + FillMergedCells = true + }; + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue122.xlsx"); + { + var q = await MiniExcel.QueryAsync(path, useHeaderRow: true, configuration: config); + var rows = q.ToList(); + Assert.Equal("HR", rows[0].Department); + Assert.Equal("HR", rows[1].Department); + Assert.Equal("HR", rows[2].Department); + Assert.Equal("IT", rows[3].Department); + Assert.Equal("IT", rows[4].Department); + Assert.Equal("IT", rows[5].Department); + } + } + + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue122_2.xlsx"); + { + var q = await MiniExcel.QueryAsync(path, useHeaderRow: true, configuration: config); + var rows = q.ToList(); + Assert.Equal("V1", rows[2].Test1); + Assert.Equal("V2", rows[5].Test2); + Assert.Equal("V3", rows[1].Test3); + Assert.Equal("V4", rows[2].Test4); + Assert.Equal("V5", rows[3].Test5); + Assert.Equal("V6", rows[5].Test5); + } + } + } + + /// + /// [Support Xlsm AutoCheck · Issue #227 · shps951023/MiniExcel] + /// (https://github.com/shps951023/MiniExcel/issues/227) + /// + [Fact] + public async Task Issue227() + { + { + var path = PathHelper.GetTempPath("xlsm"); + Assert.Throws(() => MiniExcel.SaveAs(path, new[] { new { V = "A1" }, new { V = "A2" } })); + } + + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue227.xlsm"); + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(100, rows.Count()); + + Assert.Equal(Guid.Parse("78DE23D2-DCB6-BD3D-EC67-C112BBC322A2"), rows[0].ID); + Assert.Equal("Wade", rows[0].Name); + Assert.Equal(DateTime.ParseExact("27/09/2020", "dd/MM/yyyy", CultureInfo.InvariantCulture), rows[0].BoD); + Assert.Equal(36, rows[0].Age); + Assert.False(rows[0].VIP); + Assert.Equal(decimal.Parse("5019.12"), rows[0].Points); + Assert.Equal(1, rows[0].IgnoredProperty); + } + { + using (var stream = File.OpenRead(path)) + { + var q = await stream.QueryAsync(); + var rows = q.ToList(); + Assert.Equal(100, rows.Count()); + + Assert.Equal(Guid.Parse("78DE23D2-DCB6-BD3D-EC67-C112BBC322A2"), rows[0].ID); + Assert.Equal("Wade", rows[0].Name); + Assert.Equal(DateTime.ParseExact("27/09/2020", "dd/MM/yyyy", CultureInfo.InvariantCulture), rows[0].BoD); + Assert.Equal(36, rows[0].Age); + Assert.False(rows[0].VIP); + Assert.Equal(decimal.Parse("5019.12"), rows[0].Points); + Assert.Equal(1, rows[0].IgnoredProperty); + } + } + } + + + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/226 + /// Fix SaveAsByTemplate single column demension index error #226 + /// + [Fact] + public async Task Issue226() + { + var path = PathHelper.GetTempPath(); + var templatePath = PathHelper.GetSamplePath("xlsx/TestIssue226.xlsx"); + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, new { employees = new[] { new { name = "123" }, new { name = "123" } } }); + Assert.Equal("A1:A3", Helpers.GetFirstSheetDimensionRefValue(path)); + } + + /// + /// ASP.NET Webform gridview datasource can't use miniexcel queryasdatatable · Issue #223] + /// (https://github.com/shps951023/MiniExcel/issues/223) + /// + [Fact] + public async Task Issue223() + { + var value = new List>() + { + new Dictionary(){{"A",null},{"B",null}}, + new Dictionary(){{"A",123},{"B",new DateTime(2021,1,1)}}, + new Dictionary(){{"A",Guid.NewGuid()},{"B","HelloWorld"}}, + }; + var path = PathHelper.GetTempPath(); + MiniExcel.SaveAs(path, value); + + var dt = await MiniExcel.QueryAsDataTableAsync(path); + var columns = dt.Columns; + Assert.Equal(typeof(object), columns[0].DataType); + Assert.Equal(typeof(object), columns[1].DataType); + + Assert.Equal((double)123, dt.Rows[1]["A"]); + Assert.Equal("HelloWorld", dt.Rows[2]["B"]); + } + + /// + /// [Custom yyyy-MM-dd format not convert datetime · Issue #222] + /// (https://github.com/shps951023/MiniExcel/issues/222) + /// + [Fact] + public async Task Issue222() + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue222.xlsx"); + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(typeof(DateTime), rows[1].A.GetType()); + Assert.Equal(new DateTime(2021, 4, 29), rows[1].A); + } + + /// + /// QueryAsync Support StartCell #147 + /// https://github.com/shps951023/MiniExcel/issues/147 + /// + [Fact] + public async Task Issue147() + { + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue147.xlsx"); + var q = await MiniExcel.QueryAsync(path, useHeaderRow: false, startCell: "C3", sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(new[] { "C", "D", "E" }, (rows[0] as IDictionary).Keys); + Assert.Equal(new[] { "Column1", "Column2", "Column3" }, new[] { rows[0].C as string, rows[0].D as string, rows[0].E as string }); + Assert.Equal(new[] { "C4", "D4", "E4" }, new[] { rows[1].C as string, rows[1].D as string, rows[1].E as string }); + Assert.Equal(new[] { "C9", "D9", "E9" }, new[] { rows[6].C as string, rows[6].D as string, rows[6].E as string }); + Assert.Equal(new[] { "C12", "D12", "E12" }, new[] { rows[9].C as string, rows[9].D as string, rows[9].E as string }); + Assert.Equal(new[] { "C13", "D13", "E13" }, new[] { rows[10].C as string, rows[10].D as string, rows[10].E as string }); + foreach (var i in new[] { 4, 5, 7, 8 }) + Assert.Equal(new[] { default(string), default(string), default(string) }, new[] { rows[i].C as string, rows[i].D as string, rows[i].E as string }); + + Assert.Equal(11, rows.Count); + + + var columns = MiniExcel.GetColumns(path, startCell: "C3"); + Assert.Equal(new[] { "C", "D", "E" }, columns); + } + + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue147.xlsx"); + var q = await MiniExcel.QueryAsync(path, useHeaderRow: true, startCell: "C3", sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(new[] { "Column1", "Column2", "Column3" }, (rows[0] as IDictionary).Keys); + Assert.Equal(new[] { "C4", "D4", "E4" }, new[] { rows[0].Column1 as string, rows[0].Column2 as string, rows[0].Column3 as string }); + Assert.Equal(new[] { "C9", "D9", "E9" }, new[] { rows[5].Column1 as string, rows[5].Column2 as string, rows[5].Column3 as string }); + Assert.Equal(new[] { "C12", "D12", "E12" }, new[] { rows[8].Column1 as string, rows[8].Column2 as string, rows[8].Column3 as string }); + Assert.Equal(new[] { "C13", "D13", "E13" }, new[] { rows[9].Column1 as string, rows[9].Column2 as string, rows[9].Column3 as string }); + foreach (var i in new[] { 3, 4, 6, 7 }) + Assert.Equal(new[] { default(string), default(string), default(string) }, new[] { rows[i].Column1 as string, rows[i].Column2 as string, rows[i].Column3 as string }); + + Assert.Equal(10, rows.Count); + + + var columns = MiniExcel.GetColumns(path, useHeaderRow: true, startCell: "C3"); + Assert.Equal(new[] { "Column1", "Column2", "Column3" }, columns); + } + } + + + /// + /// [Can SaveAs support iDataReader export to avoid the dataTable consuming too much memory · Issue #211 · shps951023/MiniExcel] + /// (https://github.com/shps951023/MiniExcel/issues/211) + /// + [Fact] + public async Task Issue211() + { + var path = PathHelper.GetTempPath(); + var tempSqlitePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.db"); + var connectionString = $"Data Source={tempSqlitePath};Version=3;"; + + using (var connection = new SQLiteConnection(connectionString)) + { + var reader = connection.ExecuteReader(@"select 1 Test1,2 Test2 union all select 3 , 4 union all select 5 ,6"); + + MiniExcel.SaveAs(path, reader); + + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal((double)1, rows[0].Test1); + Assert.Equal((double)2, rows[0].Test2); + Assert.Equal((double)3, rows[1].Test1); + Assert.Equal((double)4, rows[1].Test2); + } + } + + /// + /// [When reading Excel, can return IDataReader and DataTable to facilitate the import of database. Like ExcelDataReader provide reader.AsDataSet() · Issue #216 · shps951023/MiniExcel](https://github.com/shps951023/MiniExcel/issues/216) + /// + [Fact] + public async Task Issue216() + { + var path = PathHelper.GetTempPath(); + var value = new[] { new { Test1 = "1", Test2 = 2 }, new { Test1 = "3", Test2 = 4 } }; + MiniExcel.SaveAs(path, value); + + { + var table = await MiniExcel.QueryAsDataTableAsync(path); + var columns = table.Columns; + Assert.Equal("Test1", table.Columns[0].ColumnName); + Assert.Equal("Test2", table.Columns[1].ColumnName); + Assert.Equal("1", table.Rows[0]["Test1"]); + Assert.Equal((double)2, table.Rows[0]["Test2"]); + Assert.Equal("3", table.Rows[1]["Test1"]); + Assert.Equal((double)4, table.Rows[1]["Test2"]); + } + + { + var dt = await MiniExcel.QueryAsDataTableAsync(path, false); + Assert.Equal("Test1", dt.Rows[0]["A"]); + Assert.Equal("Test2", dt.Rows[0]["B"]); + Assert.Equal("1", dt.Rows[1]["A"]); + Assert.Equal((double)2, dt.Rows[1]["B"]); + Assert.Equal("3", dt.Rows[2]["A"]); + Assert.Equal((double)4, dt.Rows[2]["B"]); + } + } + + /// + /// https://gitee.com/dotnetchina/MiniExcel/issues/I3OSKV + /// When exporting, the pure numeric string will be forcibly converted to a numeric type, resulting in the loss of the end data + /// + [Fact] + public async Task IssueI3OSKV() + { + { + var path = PathHelper.GetTempPath(); + var value = new[] { new { Test = "12345678901234567890" } }; + await MiniExcel.SaveAsAsync(path, value); + + var q = await MiniExcel.QueryAsync(path, true); + var A2 = q.First().Test; + Assert.Equal("12345678901234567890", A2); + + File.Delete(path); + } + + { + var path = PathHelper.GetTempPath(); + var value = new[] { new { Test = 123456.789 } }; + await MiniExcel.SaveAsAsync(path, value); + + var q = await MiniExcel.QueryAsync(path, true); + var A2 = q.First().Test; + Assert.Equal(123456.789, A2); + + File.Delete(path); + } + } + + + /// + /// [Dynamic QueryAsync can't summary numeric cell value default, need to cast · Issue #220 · shps951023/MiniExcel] + /// (https://github.com/shps951023/MiniExcel/issues/220) + /// + [Fact] + public async Task Issue220() + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue220.xlsx"); + var rows = await MiniExcel.QueryAsync(path, useHeaderRow: true); + var result = (from s in rows + group s by s.PRT_ID into g + select new + { + PRT_ID = g.Key, + Apr = g.Sum(_ => (double?)_.Apr), + May = g.Sum(_ => (double?)_.May), + Jun = g.Sum(_ => (double?)_.Jun), + } + ).ToList(); + Assert.Equal(91843.25, result[0].Jun); + Assert.Equal(50000.99, result[1].Jun); + } + + /// + /// Optimize stream excel type check + /// https://github.com/shps951023/MiniExcel/issues/215 + /// + [Fact] + public async Task Issue215() + { + using (var stream = new MemoryStream()) + { + stream.SaveAs(new[] { new { V = "test1" }, new { V = "test2" } }); + var q = await stream.QueryAsync(true); + var rows = q.ToList(); + Assert.Equal("test1", rows[0]["V"]); + Assert.Equal("test2", rows[1]["V"]); + } + } + + /// + /// Support Enum Mapping + /// https://github.com/shps951023/MiniExcel/issues/89 + /// + [Fact] + public async Task Issue89() + { + //csv + { + var text = @"State +OnDuty +Fired +Leave"; + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(text); + writer.Flush(); + stream.Position = 0; + var q = await MiniExcel.QueryAsync(stream, excelType: ExcelType.CSV); + var rows = q.ToList(); + Assert.Equal(Issue89VO.WorkState.OnDuty, rows[0].State); + Assert.Equal(Issue89VO.WorkState.Fired, rows[1].State); + Assert.Equal(Issue89VO.WorkState.Leave, rows[2].State); + + var outputPath = PathHelper.GetTempPath("xlsx"); + MiniExcel.SaveAs(outputPath, rows); + var q2 = await MiniExcel.QueryAsync(outputPath); + var rows2 = q2.ToList(); + Assert.Equal(Issue89VO.WorkState.OnDuty, rows2[0].State); + Assert.Equal(Issue89VO.WorkState.Fired, rows2[1].State); + Assert.Equal(Issue89VO.WorkState.Leave, rows2[2].State); + } + + //xlsx + { + var path = PathHelper.GetSamplePath("xlsx/TestIssue89.xlsx"); + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(Issue89VO.WorkState.OnDuty, rows[0].State); + Assert.Equal(Issue89VO.WorkState.Fired, rows[1].State); + Assert.Equal(Issue89VO.WorkState.Leave, rows[2].State); + + var outputPath = PathHelper.GetTempPath(); + MiniExcel.SaveAs(outputPath, rows); + var q1 = await MiniExcel.QueryAsync(outputPath); + var rows2 = q1.ToList(); + Assert.Equal(Issue89VO.WorkState.OnDuty, rows2[0].State); + Assert.Equal(Issue89VO.WorkState.Fired, rows2[1].State); + Assert.Equal(Issue89VO.WorkState.Leave, rows2[2].State); + } + } + + public class Issue89VO + { + public WorkState State { get; set; } + + public enum WorkState + { + OnDuty, + Leave, + Fired + } + } + + /// + /// DataTable recommended to use Caption for column name first, then use columname + /// https://github.com/shps951023/MiniExcel/issues/217 + /// + [Fact] + public async Task Issue217() + { + DataTable table = new DataTable(); + table.Columns.Add("CustomerID"); + table.Columns.Add("CustomerName").Caption = "Name"; + table.Columns.Add("CreditLimit").Caption = "Limit"; + table.Rows.Add(new object[] { 1, "Jonathan", 23.44 }); + table.Rows.Add(new object[] { 2, "Bill", 56.87 }); + + // openxml + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + MiniExcel.SaveAs(path, table); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("Name", rows[0].B); + Assert.Equal("Limit", rows[0].C); + + + File.Delete(path); + } + + // csv + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.csv"); + await MiniExcel.SaveAsAsync(path, table); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("Name", rows[0].B); + Assert.Equal("Limit", rows[0].C); + + + File.Delete(path); + } + } + + /// + /// MiniExcel.SaveAs(path, table,sheetName:“Name”) ,the actual sheetName is Sheet1 + /// https://github.com/shps951023/MiniExcel/issues/212 + /// + [Fact] + public async Task Issue212() + { + var sheetName = "Demo"; + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + await MiniExcel.SaveAsAsync(path, new[] { new { x = 1, y = 2 } }, sheetName: sheetName); + + var actualSheetName = MiniExcel.GetSheetNames(path).ToList()[0]; + + Assert.Equal(sheetName, actualSheetName); + + File.Delete(path); + } + + /// + /// Version <= v0.13.1 Template merge row list rendering has no merge + /// https://github.com/shps951023/MiniExcel/issues/207 + /// + [Fact] + public async Task Issue207() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var tempaltePath = @"../../../../../samples/xlsx/TestIssue207_2.xlsx"; + + var value = new + { + project = new[] { + new {name = "項目1",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目2",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目3",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目4",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + } + }; + + await MiniExcel.SaveAsByTemplateAsync(path, tempaltePath, value); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("項目1", rows[0].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[0].B); + Assert.Equal("項目2", rows[2].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[2].B); + Assert.Equal("項目3", rows[4].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[4].B); + Assert.Equal("項目4", rows[6].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[6].B); + + Assert.Equal("Test1", rows[8].A); + Assert.Equal("Test2", rows[8].B); + Assert.Equal("Test3", rows[8].C); + + Assert.Equal("項目1", rows[12].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[12].B); + Assert.Equal("項目2", rows[13].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[13].B); + Assert.Equal("項目3", rows[14].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[14].B); + Assert.Equal("項目4", rows[15].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[15].B); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C16", demension); + + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var tempaltePath = @"../../../../../samples/xlsx/TestIssue207_Template_Merge_row_list_rendering_without_merge/template.xlsx"; + + var value = new + { + project = new[] { + new {name = "項目1",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目2",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目3",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + new {name = "項目4",content="[]內容1,[]內容2,[]內容3,[]內容4,[]內容5"}, + } + }; + + await MiniExcel.SaveAsByTemplateAsync(path, tempaltePath, value); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("項目1", rows[0].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[0].C); + Assert.Equal("項目2", rows[3].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[3].C); + Assert.Equal("項目3", rows[6].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[6].C); + Assert.Equal("項目4", rows[9].A); + Assert.Equal("[]內容1,[]內容2,[]內容3,[]內容4,[]內容5", rows[9].C); + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E15", demension); + + File.Delete(path); + } + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/87 + /// + [Fact] + public async Task Issue87() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateCenterEmpty.xlsx"; + var value = new + { + Tests = Enumerable.Range(1, 5).Select((s, i) => new { test1 = i, test2 = i }) + }; + using (var stream = File.OpenRead(templatePath)) + { + var q = await MiniExcel.QueryAsync(templatePath); + var rows = q.ToList(); + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + } + + File.Delete(path); + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/206 + /// + [Fact] + public async Task Issue206() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFill.xlsx"; + + var dt = new DataTable(); + { + dt.Columns.Add("name"); + dt.Columns.Add("department"); + } + var value = new Dictionary() + { + ["employees"] = dt + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B2", demension); + + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFill.xlsx"; + + var dt = new DataTable(); + { + dt.Columns.Add("name"); + dt.Columns.Add("department"); + dt.Rows.Add("Jack", "HR"); + } + var value = new Dictionary() + { + ["employees"] = dt + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B2", demension); + + File.Delete(path); + } + } + + + /// + /// https://github.com/shps951023/MiniExcel/issues/193 + /// + [Fact] + public async Task Issue193() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplexWithNamespacePrefix.xlsx"; + + // 1. By Class + var value = new + { + title = "FooCompany", + managers = new[] { + new {name="Jack",department="HR"}, + new {name="Loan",department="IT"} + }, + employees = new[] { + new {name="Wade",department="HR"}, + new {name="Felix",department="HR"}, + new {name="Eric",department="IT"}, + new {name="Keaton",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + + + foreach (var sheetName in MiniExcel.GetSheetNames(path)) + { + var q = await MiniExcel.QueryAsync(path, sheetName: sheetName); + var rows = q.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); + + //TODO:row can't contain xmlns + //![image](https://user-images.githubusercontent.com/12729184/114998840-ead44500-9ed3-11eb-8611-58afb98faed9.png) + + } + + File.Delete(path); + } + + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + + // 2. By Dictionary + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = new[] { + new {name="Jack",department="HR"}, + new {name="Loan",department="IT"} + }, + ["employees"] = new[] { + new {name="Wade",department="HR"}, + new {name="Felix",department="HR"}, + new {name="Eric",department="IT"}, + new {name="Keaton",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + 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); + + File.Delete(path); + } + + } + + [Fact] + public async Task Issue142() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + MiniExcel.SaveAs(path, new Issue142VO[] { new Issue142VO { MyProperty1 = "MyProperty1", MyProperty2 = "MyProperty2", MyProperty3 = "MyProperty3", MyProperty4 = "MyProperty4", MyProperty5 = "MyProperty5", MyProperty6 = "MyProperty6", MyProperty7 = "MyProperty7" } }); + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("MyProperty4", rows[0].A); + Assert.Equal("CustomColumnName", rows[0].B); //note + Assert.Equal("MyProperty5", rows[0].C); + Assert.Equal("MyProperty2", rows[0].D); + Assert.Equal("MyProperty6", rows[0].E); + Assert.Equal(null, rows[0].F); + Assert.Equal("MyProperty3", rows[0].G); + + Assert.Equal("MyProperty4", rows[0].A); + Assert.Equal("CustomColumnName", rows[0].B); //note + Assert.Equal("MyProperty5", rows[0].C); + Assert.Equal("MyProperty2", rows[0].D); + Assert.Equal("MyProperty6", rows[0].E); + Assert.Equal(null, rows[0].F); + Assert.Equal("MyProperty3", rows[0].G); + } + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + + Assert.Equal("MyProperty4", rows[0].MyProperty4); + Assert.Equal("MyProperty1", rows[0].MyProperty1); //note + Assert.Equal("MyProperty5", rows[0].MyProperty5); + Assert.Equal("MyProperty2", rows[0].MyProperty2); + Assert.Equal("MyProperty6", rows[0].MyProperty6); + Assert.Null(rows[0].MyProperty7); + Assert.Equal("MyProperty3", rows[0].MyProperty3); + } + + File.Delete(path); + + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); + MiniExcel.SaveAs(path, new Issue142VO[] { new Issue142VO { MyProperty1 = "MyProperty1", MyProperty2 = "MyProperty2", MyProperty3 = "MyProperty3", MyProperty4 = "MyProperty4", MyProperty5 = "MyProperty5", MyProperty6 = "MyProperty6", MyProperty7 = "MyProperty7" } }); + var expected = @"MyProperty4,CustomColumnName,MyProperty5,MyProperty2,MyProperty6,,MyProperty3 +MyProperty4,MyProperty1,MyProperty5,MyProperty2,MyProperty6,,MyProperty3 +"; + Assert.Equal(expected, File.ReadAllText(path)); + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + + Assert.Equal("MyProperty4", rows[0].MyProperty4); + Assert.Equal("MyProperty1", rows[0].MyProperty1); //note + Assert.Equal("MyProperty5", rows[0].MyProperty5); + Assert.Equal("MyProperty2", rows[0].MyProperty2); + Assert.Equal("MyProperty6", rows[0].MyProperty6); + Assert.Null(rows[0].MyProperty7); + Assert.Equal("MyProperty3", rows[0].MyProperty3); + } + + File.Delete(path); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); + var input = new Issue142VoDuplicateColumnName[] { new Issue142VoDuplicateColumnName { } }; + Assert.Throws(() => MiniExcel.SaveAs(path, input)); + } + } + + [Fact] + public async Task Issue142_Query() + { + { + var path = @"../../../../../samples/xlsx/TestIssue142.xlsx"; + await Assert.ThrowsAsync(async () => + { + var q = await MiniExcel.QueryAsync(path); + q.ToList(); + }); + } + + { + var path = @"../../../../../samples/xlsx/TestIssue142.xlsx"; + await Assert.ThrowsAsync(async () => + { + var q = await MiniExcel.QueryAsync(path); + q.ToList(); + }); + } + + { + var path = @"../../../../../samples/xlsx/TestIssue142.xlsx"; + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("CustomColumnName", rows[0].MyProperty1); + Assert.Null(rows[0].MyProperty7); + Assert.Equal("MyProperty2", rows[0].MyProperty2); + Assert.Equal("MyProperty103", rows[0].MyProperty3); + Assert.Equal("MyProperty100", rows[0].MyProperty4); + Assert.Equal("MyProperty102", rows[0].MyProperty5); + Assert.Equal("MyProperty6", rows[0].MyProperty6); + } + + { + var path = @"../../../../../samples/csv/TestIssue142.csv"; + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal("CustomColumnName", rows[0].MyProperty1); + Assert.Null(rows[0].MyProperty7); + Assert.Equal("MyProperty2", rows[0].MyProperty2); + Assert.Equal("MyProperty103", rows[0].MyProperty3); + Assert.Equal("MyProperty100", rows[0].MyProperty4); + Assert.Equal("MyProperty102", rows[0].MyProperty5); + Assert.Equal("MyProperty6", rows[0].MyProperty6); + } + } + + public class Issue142VO + { + [ExcelColumnName("CustomColumnName")] + public string MyProperty1 { get; set; } //index = 1 + [ExcelIgnore] + public string MyProperty7 { get; set; } //index = null + public string MyProperty2 { get; set; } //index = 3 + [ExcelColumnIndex(6)] + public string MyProperty3 { get; set; } //index = 6 + [ExcelColumnIndex("A")] // equal column index 0 + public string MyProperty4 { get; set; } + [ExcelColumnIndex(2)] + public string MyProperty5 { get; set; } //index = 2 + public string MyProperty6 { get; set; } //index = 4 + } + + public class Issue142VoDuplicateColumnName + { + [ExcelColumnIndex("A")] + public int MyProperty1 { get; set; } + [ExcelColumnIndex("A")] + public int MyProperty2 { get; set; } + + public int MyProperty3 { get; set; } + [ExcelColumnIndex("B")] + public int MyProperty4 { get; set; } + } + + public class Issue142VoOverIndex + { + [ExcelColumnIndex("Z")] + public int MyProperty1 { get; set; } + } + + public class Issue142VoExcelColumnNameNotFound + { + [ExcelColumnIndex("B")] + public int MyProperty1 { get; set; } + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/150 + /// + [Fact] + public async Task Issue150() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + //MiniExcel.SaveAs(path, new[] { "1", "2" }); + await Assert.ThrowsAnyAsync(async() => await MiniExcel.SaveAsAsync(path, new[] { 1, 2 })); + File.Delete(path); + await Assert.ThrowsAnyAsync(async () => await MiniExcel.SaveAsAsync(path, new[] { "1", "2" })); + File.Delete(path); + await Assert.ThrowsAnyAsync(async () => await MiniExcel.SaveAsAsync(path, new[] { '1', '2' })); + File.Delete(path); + await Assert.ThrowsAnyAsync(async () => await MiniExcel.SaveAsAsync(path, new[] { DateTime.Now })); + File.Delete(path); + await Assert.ThrowsAnyAsync(async () => await MiniExcel.SaveAsAsync(path, new[] { Guid.NewGuid() })); + File.Delete(path); + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/157 + /// + [Fact] + public async Task Issue157() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + Console.WriteLine("==== SaveAs by strongly type ===="); + var input = JsonConvert.DeserializeObject>("[{\"ID\":\"78de23d2-dcb6-bd3d-ec67-c112bbc322a2\",\"Name\":\"Wade\",\"BoD\":\"2020-09-27T00:00:00\",\"Age\":5019,\"VIP\":false,\"Points\":5019.12,\"IgnoredProperty\":null},{\"ID\":\"20d3bfce-27c3-ad3e-4f70-35c81c7e8e45\",\"Name\":\"Felix\",\"BoD\":\"2020-10-25T00:00:00\",\"Age\":7028,\"VIP\":true,\"Points\":7028.46,\"IgnoredProperty\":null},{\"ID\":\"52013bf0-9aeb-48e6-e5f5-e9500afb034f\",\"Name\":\"Phelan\",\"BoD\":\"2021-10-04T00:00:00\",\"Age\":3836,\"VIP\":true,\"Points\":3835.7,\"IgnoredProperty\":null},{\"ID\":\"3b97b87c-7afe-664f-1af5-6914d313ae25\",\"Name\":\"Samuel\",\"BoD\":\"2020-06-21T00:00:00\",\"Age\":9352,\"VIP\":false,\"Points\":9351.71,\"IgnoredProperty\":null},{\"ID\":\"9a989c43-d55f-5306-0d2f-0fbafae135bb\",\"Name\":\"Raymond\",\"BoD\":\"2021-07-12T00:00:00\",\"Age\":8210,\"VIP\":true,\"Points\":8209.76,\"IgnoredProperty\":null}]"); + await MiniExcel.SaveAsAsync(path, input); + + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(6, rows.Count()); + Assert.Equal("Sheet1", MiniExcel.GetSheetNames(path).First()); + + using (var p = new ExcelPackage(new FileInfo(path))) + { + var ws = p.Workbook.Worksheets.First(); + Assert.Equal("Sheet1", ws.Name); + Assert.Equal("Sheet1", p.Workbook.Worksheets["Sheet1"].Name); + } + } + { + var path = @"../../../../../samples/xlsx/TestIssue157.xlsx"; + + { + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(6, rows.Count()); + Assert.Equal("Sheet1", MiniExcel.GetSheetNames(path).First()); + } + using (var p = new ExcelPackage(new FileInfo(path))) + { + var ws = p.Workbook.Worksheets.First(); + Assert.Equal("Sheet1", ws.Name); + Assert.Equal("Sheet1", p.Workbook.Worksheets["Sheet1"].Name); + } + + using (var stream = File.OpenRead(path)) + { + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(5, rows.Count()); + + Assert.Equal(Guid.Parse("78DE23D2-DCB6-BD3D-EC67-C112BBC322A2"), rows[0].ID); + Assert.Equal("Wade", rows[0].Name); + Assert.Equal(DateTime.ParseExact("27/09/2020", "dd/MM/yyyy", CultureInfo.InvariantCulture), rows[0].BoD); + Assert.False(rows[0].VIP); + Assert.Equal(decimal.Parse("5019.12"), rows[0].Points); + Assert.Equal(1, rows[0].IgnoredProperty); + } + } + + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/149 + /// + [Fact] + public async Task Issue149() + { + var chars = new char[] {'\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007','\u0008', + '\u0009', // + '\u000A', // + '\u000B','\u000C', + '\u000D', // + '\u000E','\u000F','\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016', + '\u0017','\u0018','\u0019','\u001A','\u001B','\u001C','\u001D','\u001E','\u001F','\u007F' + }.Select(s => s.ToString()).ToArray(); + + { + var path = @"../../../../../samples/xlsx/TestIssue149.xlsx"; + var q = await MiniExcel.QueryAsync(path); + var rows = q.Select(s => (string)s.A).ToList(); + for (int i = 0; i < chars.Length; i++) + { + //output.WriteLine($"{i} , {chars[i]} , {rows[i]}"); + if (i == 13) + continue; + Assert.Equal(chars[i], rows[i]); + } + } + + { + string path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var input = chars.Select(s => new { Test = s.ToString() }); + await MiniExcel.SaveAsAsync(path, input); + + var q = await MiniExcel.QueryAsync(path, true); + + var rows = q.Select(s => (string)s.Test).ToList(); + for (int i = 0; i < chars.Length; i++) + { + output.WriteLine($"{i} , {chars[i]} , {rows[i]}"); + if (i == 13 || i == 9 || i == 10) + continue; + Assert.Equal(chars[i], rows[i]); + } + } + + { + string path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); + var input = chars.Select(s => new { Test = s.ToString() }); + await MiniExcel.SaveAsAsync(path, input); + + var q = await MiniExcel.QueryAsync(path); + var rows = q.Select(s => (string)s.Test).ToList(); + for (int i = 0; i < chars.Length; i++) + { + output.WriteLine($"{i} , {chars[i]} , {rows[i]}"); + if (i == 13 || i == 9 || i == 10) + continue; + Assert.Equal(chars[i], rows[i]); + } + } + } + + public class Issue149VO + { + public string Test { get; set; } + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/153 + /// + [Fact] + public async Task Issue153() + { + var path = @"../../../../../samples/xlsx/TestIssue153.xlsx"; + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.First() as IDictionary; + + Assert.Equal(new[] { "序号", "代号", "新代号", "名称", "XXX", "部门名称", "单位", "ERP工时 (小时)A", "工时(秒) A/3600", "标准人工工时(秒)", "生产标准机器工时(秒)", "财务、标准机器工时(秒)", "更新日期", "产品机种", "备注", "最近一次修改前的标准工时(秒)", "最近一次修改前的标准机时(秒)", "备注1" } + , rows.Keys); + } + + /// + /// https://github.com/shps951023/MiniExcel/issues/137 + /// + [Fact] + public async Task Issue137() + { + var path = @"../../../../../samples/xlsx/TestIssue137.xlsx"; + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + var first = rows[0] as IDictionary; //![image](https://user-images.githubusercontent.com/12729184/113266322-ba06e400-9307-11eb-9521-d36abfda75cc.png) + Assert.Equal(new[] { "A", "B", "C", "D", "E", "F", "G", "H" }, first.Keys.ToArray()); + Assert.Equal(11, rows.Count); + { + var row = rows[0] as IDictionary; + Assert.Equal("比例", row["A"]); + Assert.Equal("商品", row["B"]); + Assert.Equal("滿倉口數", row["C"]); + Assert.Equal(" ", row["D"]); + Assert.Equal(" ", row["E"]); + Assert.Equal(" ", row["F"]); + Assert.Equal(Double.Parse("0"), row["G"]); + Assert.Equal("1為港幣 0為台幣", row["H"]); + } + { + var row = rows[1] as IDictionary; + Assert.Equal(double.Parse("1"), row["A"]); + Assert.Equal("MTX", row["B"]); + Assert.Equal(double.Parse("10"), row["C"]); + Assert.Null(row["D"]); + Assert.Null(row["E"]); + Assert.Null(row["F"]); + Assert.Null(row["G"]); + Assert.Null(row["H"]); + } + { + var row = rows[2] as IDictionary; + Assert.Equal(double.Parse("0.95"), row["A"]); + } + } + + // dynamic query with head + { + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + var first = rows[0] as IDictionary; //![image](https://user-images.githubusercontent.com/12729184/113266322-ba06e400-9307-11eb-9521-d36abfda75cc.png) + Assert.Equal(new[] { "比例", "商品", "滿倉口數", "0", "1為港幣 0為台幣" }, first.Keys.ToArray()); + Assert.Equal(10, rows.Count); + { + var row = rows[0] as IDictionary; + Assert.Equal(double.Parse("1"), row["比例"]); + Assert.Equal("MTX", row["商品"]); + Assert.Equal(double.Parse("10"), row["滿倉口數"]); + Assert.Null(row["0"]); + Assert.Null(row["1為港幣 0為台幣"]); + } + + { + var row = rows[1] as IDictionary; + Assert.Equal(double.Parse("0.95"), row["比例"]); + } + } + + { + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(10, rows.Count); + { + var row = rows[0]; + Assert.Equal(1, row.比例); + Assert.Equal("MTX", row.商品); + Assert.Equal(10, row.滿倉口數); + } + + { + var row = rows[1]; + Assert.Equal(0.95, row.比例); + } + } + } + + public class Issue137ExcelRow + { + public double? 比例 { get; set; } + public string 商品 { get; set; } + public int? 滿倉口數 { get; set; } + } + + + /// + /// https://github.com/shps951023/MiniExcel/issues/138 + /// + [Fact] + public async Task Issue138() + { + var path = @"../../../../../samples/xlsx/TestIssue138.xlsx"; + { + var q = await MiniExcel.QueryAsync(path, true); + var rows = q.ToList(); + Assert.Equal(6, rows.Count); + + foreach (var index in new[] { 0, 2, 5 }) + { + Assert.Equal(1, rows[index].實單每日損益); + Assert.Equal(2, rows[index].程式每日損益); + Assert.Equal("測試商品1", rows[index].商品); + Assert.Equal(111.11, rows[index].滿倉口數); + Assert.Equal(111.11, rows[index].波段); + Assert.Equal(111.11, rows[index].當沖); + } + + foreach (var index in new[] { 1, 3, 4 }) + { + Assert.Null(rows[index].實單每日損益); + Assert.Null(rows[index].程式每日損益); + Assert.Null(rows[index].商品); + Assert.Null(rows[index].滿倉口數); + Assert.Null(rows[index].波段); + Assert.Null(rows[index].當沖); + } + } + { + + var q = await MiniExcel.QueryAsync(path); + var rows = q.ToList(); + Assert.Equal(6, rows.Count); + Assert.Equal(new DateTime(2021, 3, 1), rows[0].date); + + foreach (var index in new[] { 0, 2, 5 }) + { + Assert.Equal(1, rows[index].實單每日損益); + Assert.Equal(2, rows[index].程式每日損益); + Assert.Equal("測試商品1", rows[index].商品); + Assert.Equal(111.11, rows[index].滿倉口數); + Assert.Equal(111.11, rows[index].波段); + Assert.Equal(111.11, rows[index].當沖); + } + + foreach (var index in new[] { 1, 3, 4 }) + { + Assert.Null(rows[index].實單每日損益); + Assert.Null(rows[index].程式每日損益); + Assert.Null(rows[index].商品); + Assert.Null(rows[index].滿倉口數); + Assert.Null(rows[index].波段); + Assert.Null(rows[index].當沖); + } + } + } + + public class Issue138ExcelRow + { + public DateTime? date { get; set; } + public int? 實單每日損益 { get; set; } + public int? 程式每日損益 { get; set; } + public string 商品 { get; set; } + public double? 滿倉口數 { get; set; } + public double? 波段 { get; set; } + public double? 當沖 { get; set; } + } + } +} \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlMultipleSheetAsyncTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlMultipleSheetAsyncTests.cs new file mode 100644 index 0000000..9498411 --- /dev/null +++ b/tests/MiniExcelTests/MiniExcelOpenXmlMultipleSheetAsyncTests.cs @@ -0,0 +1,115 @@ +using Xunit; +using System.Linq; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace MiniExcelLibs.Tests +{ + public partial class MiniExcelOpenXmlMultipleSheetAsyncTests + { + [Fact] + public async Task SpecifySheetNameQueryTest() + { + var path = @"../../../../../samples/xlsx/TestMultiSheet.xlsx"; + { + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet3"); + var rows = q.ToList(); + Assert.Equal(5, rows.Count); + Assert.Equal(3, rows[0].A); + Assert.Equal(3, rows[0].B); + } + { + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet2"); + var rows = q.ToList(); + Assert.Equal(12, rows.Count); + Assert.Equal(1, rows[0].A); + Assert.Equal(1, rows[0].B); + } + { + var q = await MiniExcel.QueryAsync(path, sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(12, rows.Count); + Assert.Equal(2, rows[0].A); + Assert.Equal(2, rows[0].B); + } + { + await Assert.ThrowsAsync(async() => { + var q= await MiniExcel.QueryAsync(path, sheetName: "xxxx"); + q.ToList(); + }); + } + + using (var stream = File.OpenRead(path)) + { + { + var q = await stream.QueryAsync(sheetName: "Sheet3"); + var rows = q.ToList(); + Assert.Equal(5, rows.Count); + Assert.Equal(3d, rows[0]["A"]); + Assert.Equal(3d, rows[0]["B"]); + } + { + var q = await stream.QueryAsync(sheetName: "Sheet2"); + var rows = q.ToList(); + Assert.Equal(12, rows.Count); + Assert.Equal(1d, rows[0]["A"]); + Assert.Equal(1d, rows[0]["B"]); + } + { + var q = await stream.QueryAsync(sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(12, rows.Count); + Assert.Equal(2d, rows[0]["A"]); + Assert.Equal(2d, rows[0]["B"]); + } + { + var q = await stream.QueryAsync(sheetName: "Sheet1"); + var rows = q.ToList(); + Assert.Equal(12, rows.Count); + Assert.Equal(2d, rows[0]["A"]); + Assert.Equal(2d, rows[0]["B"]); + } + } + } + + [Fact] + public async Task MultiSheetsQueryBasicTest() + { + var path = @"../../../../../samples/xlsx/TestMultiSheet.xlsx"; + using (var stream = File.OpenRead(path)) + { + var sheet1 = await stream.QueryAsync(sheetName: "Sheet1"); + var sheet2 = await stream.QueryAsync(sheetName: "Sheet2"); + var sheet3 = await stream.QueryAsync(sheetName: "Sheet3"); + } + } + + [Fact] + public async Task MultiSheetsQueryTest() + { + var path = @"../../../../../samples/xlsx/TestMultiSheet.xlsx"; + { + var sheetNames = MiniExcel.GetSheetNames(path).ToList(); + foreach (var sheetName in sheetNames) + { + var rows = await MiniExcel.QueryAsync(path, sheetName: sheetName); + } + + Assert.Equal(new[] { "Sheet2", "Sheet1", "Sheet3" }, sheetNames); + } + + { + using (var stream = File.OpenRead(path)) + { + var sheetNames = stream.GetSheetNames().ToList(); + Assert.Equal(new[] { "Sheet2", "Sheet1", "Sheet3" }, sheetNames); + foreach (var sheetName in sheetNames) + { + var rows = await stream.QueryAsync(sheetName: sheetName); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs b/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs new file mode 100644 index 0000000..158b5df --- /dev/null +++ b/tests/MiniExcelTests/MiniExcelTemplateAsyncTests.cs @@ -0,0 +1,739 @@ +using Dapper; +using MiniExcelLibs; +using MiniExcelLibs.Tests.Utils; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace MiniExcelTests +{ + public class MiniExcelTemplateAsyncTests + { + [Fact] + public async Task DatatableTemptyRowTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + var managers = new DataTable(); + { + managers.Columns.Add("name"); + managers.Columns.Add("department"); + } + var employees = new DataTable(); + { + employees.Columns.Add("name"); + employees.Columns.Add("department"); + } + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = managers, + ["employees"] = employees + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + { + var rows = MiniExcel.Query(path).ToList(); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C5", demension); + } + } + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + var managers = new DataTable(); + { + managers.Columns.Add("name"); + managers.Columns.Add("department"); + managers.Rows.Add("Jack", "HR"); + } + var employees = new DataTable(); + { + employees.Columns.Add("name"); + employees.Columns.Add("department"); + employees.Rows.Add("Wade", "HR"); + } + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = managers, + ["employees"] = employees + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + { + var rows = MiniExcel.Query(path).ToList(); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C5", demension); + } + } + } + + [Fact] + public async Task DatatableTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + var managers = new DataTable(); + { + managers.Columns.Add("name"); + managers.Columns.Add("department"); + managers.Rows.Add("Jack", "HR"); + managers.Rows.Add("Loan", "IT"); + } + var employees = new DataTable(); + { + employees.Columns.Add("name"); + employees.Columns.Add("department"); + employees.Rows.Add("Wade", "HR"); + employees.Rows.Add("Felix", "HR"); + employees.Rows.Add("Eric", "IT"); + employees.Rows.Add("Keaton", "IT"); + } + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = managers, + ["employees"] = employees + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + { + var rows = MiniExcel.Query(path).ToList(); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:C9", demension); + + 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 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 async Task 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'") + }; + await MiniExcel.SaveAsByTemplateAsync(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 async Task 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"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(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 async Task TestGithubProject() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateGithubProjects.xlsx"; + var projects = new[] + { + new {Name = "MiniExcel",Link="https://github.com/shps951023/MiniExcel",Star=146, CreateTime=new DateTime(2021,03,01)}, + new {Name = "HtmlTableHelper",Link="https://github.com/shps951023/HtmlTableHelper",Star=16, CreateTime=new DateTime(2020,02,01)}, + new {Name = "PocoClassGenerator",Link="https://github.com/shps951023/PocoClassGenerator",Star=16, CreateTime=new DateTime(2019,03,17)} + }; + var value = new + { + User = "ITWeiHan", + Projects = projects, + TotalStar = projects.Sum(s => s.Star) + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("ITWeiHan Github Projects", rows[0].B); + Assert.Equal("Total Star : 178", rows[8].C); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:D9", demension); + } + + public class TestIEnumerableTypePoco + { + public string @string { get; set; } + public int? @int { get; set; } + public decimal? @decimal { get; set; } + public double? @double { get; set; } + public DateTime? datetime { get; set; } + public bool? @bool { get; set; } + public Guid? Guid { get; set; } + } + [Fact] + public async Task TestIEnumerableType() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestIEnumerableType.xlsx"; + + //1. By POCO + var poco = new TestIEnumerableTypePoco { @string = "string", @int = 123, @decimal = decimal.Parse("123.45"), @double = (double)123.33, @datetime = new DateTime(2021, 4, 1), @bool = true, @Guid = Guid.NewGuid() }; + var value = new + { + Ts = new[] { + poco, + new TestIEnumerableTypePoco{}, + null, + new TestIEnumerableTypePoco{}, + poco + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal(poco.@string, rows[0].@string); + Assert.Equal(poco.@int, rows[0].@int); + Assert.Equal(poco.@double, rows[0].@double); + Assert.Equal(poco.@decimal, rows[0].@decimal); + Assert.Equal(poco.@bool, rows[0].@bool); + Assert.Equal(poco.datetime, rows[0].datetime); + Assert.Equal(poco.Guid, rows[0].Guid); + + Assert.Null(rows[1].@string); + Assert.Null(rows[1].@int); + Assert.Null(rows[1].@double); + Assert.Null(rows[1].@decimal); + Assert.Null(rows[1].@bool); + Assert.Null(rows[1].datetime); + Assert.Null(rows[1].Guid); + + // special input null but query is empty vo + Assert.Null(rows[2].@string); + Assert.Null(rows[2].@int); + Assert.Null(rows[2].@double); + Assert.Null(rows[2].@decimal); + Assert.Null(rows[2].@bool); + Assert.Null(rows[2].datetime); + Assert.Null(rows[2].Guid); + + Assert.Null(rows[3].@string); + Assert.Null(rows[3].@int); + Assert.Null(rows[3].@double); + Assert.Null(rows[3].@decimal); + Assert.Null(rows[3].@bool); + Assert.Null(rows[3].datetime); + Assert.Null(rows[3].Guid); + + + Assert.Equal(poco.@string, rows[4].@string); + Assert.Equal(poco.@int, rows[4].@int); + Assert.Equal(poco.@double, rows[4].@double); + Assert.Equal(poco.@decimal, rows[4].@decimal); + Assert.Equal(poco.@bool, rows[4].@bool); + Assert.Equal(poco.datetime, rows[4].datetime); + Assert.Equal(poco.Guid, rows[4].Guid); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:G6", demension); + } + } + + [Fact] + public async Task TestTemplateTypeMapping() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestITemplateTypeAutoMapping.xlsx"; + + //1. By POCO + var value = new TestIEnumerableTypePoco { @string = "string", @int = 123, @decimal = decimal.Parse("123.45"), @double = (double)123.33, @datetime = new DateTime(2021, 4, 1), @bool = true, @Guid = Guid.NewGuid() }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal(value.@string, rows[0].@string); + Assert.Equal(value.@int, rows[0].@int); + Assert.Equal(value.@double, rows[0].@double); + Assert.Equal(value.@decimal, rows[0].@decimal); + Assert.Equal(value.@bool, rows[0].@bool); + Assert.Equal(value.datetime, rows[0].datetime); + Assert.Equal(value.Guid, rows[0].Guid); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:G2", demension); + } + } + + [Fact] + public async Task TemplateCenterEmptyTest() + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateCenterEmpty.xlsx"; + var value = new + { + Tests = Enumerable.Range(1, 5).Select((s, i) => new { test1 = i, test2 = i }) + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + } + + [Fact] + public async Task TemplateAsyncBasiTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + var templateBytes = File.ReadAllBytes(templatePath); + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + await MiniExcel.SaveAsByTemplateAsync(path, templateBytes, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + var templateBytes = File.ReadAllBytes(templatePath); + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + using (var stream = File.Create(path)) + { + await stream.SaveAsByTemplateAsync(templateBytes, value); + } + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + // 2. By Dictionary + var value = new Dictionary() + { + ["Name"] = "Jack", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123 + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + } + + [Fact] + public async Task TestIEnumerable() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFill.xlsx"; + + //1. By POCO + var value = new + { + employees = new[] { + new {name="Jack",department="HR"}, + new {name="Lisa",department="HR"}, + new {name="John",department="HR"}, + new {name="Mike",department="IT"}, + new {name="Neo",department="IT"}, + new {name="Loan",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B7", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFill.xlsx"; + + //2. By Dictionary + var value = new Dictionary() + { + ["employees"] = new[] { + new {name="Jack",department="HR"}, + new {name="Lisa",department="HR"}, + new {name="John",department="HR"}, + new {name="Mike",department="IT"}, + new {name="Neo",department="IT"}, + new {name="Loan",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B7", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateBasicIEmumerableFill.xlsx"; + + //3. By DataTable + var dt = new DataTable(); + { + dt.Columns.Add("name"); + dt.Columns.Add("department"); + dt.Rows.Add("Jack", "HR"); + dt.Rows.Add("Lisa", "HR"); + dt.Rows.Add("John", "HR"); + dt.Rows.Add("Mike", "IT"); + dt.Rows.Add("Neo", "IT"); + dt.Rows.Add("Loan", "IT"); + } + var value = new Dictionary() + { + ["employees"] = dt + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:B7", demension); + } + } + + [Fact] + public async Task TemplateTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + + // 1. By Class + var value = new + { + title = "FooCompany", + managers = new[] { + new {name="Jack",department="HR"}, + new {name="Loan",department="IT"} + }, + employees = new[] { + new {name="Wade",department="HR"}, + new {name="Felix",department="HR"}, + new {name="Eric",department="IT"}, + new {name="Keaton",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(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); + } + } + + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateComplex.xlsx"; + + // 2. By Dictionary + var value = new Dictionary() + { + ["title"] = "FooCompany", + ["managers"] = new[] { + new {name="Jack",department="HR"}, + new {name="Loan",department="IT"} + }, + ["employees"] = new[] { + new {name="Wade",department="HR"}, + new {name="Felix",department="HR"}, + new {name="Eric",department="IT"}, + new {name="Keaton",department="IT"} + } + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + 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); + } + + } + } +} diff --git a/tests/MiniExcelTests/MiniExcelTemplateTests.cs b/tests/MiniExcelTests/MiniExcelTemplateTests.cs index 607702d..85ae721 100644 --- a/tests/MiniExcelTests/MiniExcelTemplateTests.cs +++ b/tests/MiniExcelTests/MiniExcelTemplateTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace MiniExcelTests @@ -541,6 +542,111 @@ namespace MiniExcelTests } } + [Fact] + public async Task TemplateAsyncBasiTest() + { + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + var templateBytes = File.ReadAllBytes(templatePath); + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + await MiniExcel.SaveAsByTemplateAsync(path, templateBytes, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + var templateBytes = File.ReadAllBytes(templatePath); + // 1. By POCO + var value = new + { + Name = "Jack", + CreateDate = new DateTime(2021, 01, 01), + VIP = true, + Points = 123 + }; + using (var stream = File.Create(path)) + { + await stream.SaveAsByTemplateAsync(templateBytes, value); + } + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + + { + var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); + var templatePath = @"../../../../../samples/xlsx/TestTemplateEasyFill.xlsx"; + // 2. By Dictionary + var value = new Dictionary() + { + ["Name"] = "Jack", + ["CreateDate"] = new DateTime(2021, 01, 01), + ["VIP"] = true, + ["Points"] = 123 + }; + MiniExcel.SaveAsByTemplate(path, templatePath, value); + + var rows = MiniExcel.Query(path).ToList(); + Assert.Equal("Jack", rows[1].A); + Assert.Equal("2021-01-01 00:00:00", rows[1].B); + Assert.Equal(true, rows[1].C); + Assert.Equal(123, rows[1].D); + Assert.Equal("Jack has 123 points", rows[1].E); + + var demension = Helpers.GetFirstSheetDimensionRefValue(path); + Assert.Equal("A1:E2", demension); + } + } + [Fact] public void TestIEnumerable() { diff --git a/tests/MiniExcelTests/Utils/PathHelper.cs b/tests/MiniExcelTests/Utils/PathHelper.cs index 2e55aea..10d60c1 100644 --- a/tests/MiniExcelTests/Utils/PathHelper.cs +++ b/tests/MiniExcelTests/Utils/PathHelper.cs @@ -14,7 +14,7 @@ { var method = (new System.Diagnostics.StackTrace()).GetFrame(1).GetMethod(); - var path = Path.Combine(Path.GetTempPath(), $"{method.DeclaringType.Name}_{method.Name}.{extension}"); + var path = Path.Combine(Path.GetTempPath(), $"{method.DeclaringType.Name}_{method.Name}.{extension}").Replace("<", string.Empty).Replace(">", string.Empty); if (File.Exists(path)) File.Delete(path); return path;