diff --git a/docs/README.md b/docs/README.md index 318b3670..5594b455 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,10 @@ ## Release Notes +### 0.11.1 +- [New] Support Support ColumnIndex Attribute [#142](https://github.com/shps951023/MiniExcel/issues/142) & [#I3I3EB](https://gitee.com/dotnetchina/MiniExcel/issues/I3I3EB) +- [Bug] Fix issue #157 : Special conditions will get the wrong worksheet name +- [Update] issue #150 : SaveAs input IEnuerable should throw clear msg exception + ### 0.11.0 - [New] Added GetSheetNames method support multi-sheets Query - [New] Query support by sheet name diff --git a/samples/csv/TestIssue142.csv b/samples/csv/TestIssue142.csv new file mode 100644 index 00000000..b90a0a44 --- /dev/null +++ b/samples/csv/TestIssue142.csv @@ -0,0 +1,2 @@ +MyProperty100,,MyProperty102,,CustomColumnName,,MyProperty103,MyProperty6,MyProperty2,,MyProperty3,MyProperty7,MyProperty1,MyProperty5,MyProperty4 +MyProperty100,,MyProperty102,,CustomColumnName,,MyProperty103,MyProperty6,MyProperty2,,MyProperty3,MyProperty7,MyProperty1,MyProperty5,MyProperty4 diff --git a/samples/xlsx/TestIssue142.xlsx b/samples/xlsx/TestIssue142.xlsx new file mode 100644 index 00000000..7c03557d Binary files /dev/null and b/samples/xlsx/TestIssue142.xlsx differ diff --git a/src/MiniExcel/Csv/CsvReader.cs b/src/MiniExcel/Csv/CsvReader.cs index 9ee880d1..1949dc93 100644 --- a/src/MiniExcel/Csv/CsvReader.cs +++ b/src/MiniExcel/Csv/CsvReader.cs @@ -66,7 +66,7 @@ public IEnumerable> Query(bool useHeaderRow, string var cf = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration; var type = typeof(T); - var props = Helpers.GetSaveAsProperties(type); + Dictionary idxProps = new Dictionary(); using (var reader = cf.GetStreamReaderFunc(_stream)) { @@ -79,6 +79,7 @@ public IEnumerable> Query(bool useHeaderRow, string { row = reader.ReadLine(); read = row.Split(seperators, StringSplitOptions.None); + var props = Helpers.GetExcelCustomPropertyInfos(type, read); var index = 0; foreach (var v in read) { diff --git a/src/MiniExcel/MiniExcelLibs.csproj b/src/MiniExcel/MiniExcelLibs.csproj index 3c2baad3..f82a0966 100644 --- a/src/MiniExcel/MiniExcelLibs.csproj +++ b/src/MiniExcel/MiniExcelLibs.csproj @@ -19,7 +19,7 @@ https://github.com/shps951023/MiniExcel https://raw.githubusercontent.com/shps951023/ImageHosting/master/img/2019-01-17.13.18.32-image.png net461;netstandard2.0;net5.0 - 0.11.0 + 0.11.1 Please Check [Release Notes](https://github.com/shps951023/MiniExcel/tree/master/docs) Github diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs index 595d1f35..7065acb4 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Xml; using System.Xml.Linq; +using static MiniExcelLibs.Utils.Helpers; namespace MiniExcelLibs.OpenXml { @@ -47,7 +48,7 @@ public IEnumerable> Query(bool UseHeaderRow, string var s = _sheetRecords.SingleOrDefault(_ => _.Name == sheetName); if (s == null) throw new InvalidOperationException("Please check sheetName/Index is correct"); - sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}" || w.FullName == s.Path || s.Path == $"/{w.FullName}" ); + sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}" || w.FullName == s.Path || s.Path == $"/{w.FullName}"); } else if (sheets.Count() > 1) { @@ -310,12 +311,22 @@ public IEnumerable> Query(bool UseHeaderRow, string public IEnumerable Query(string sheetName, IConfiguration configuration) where T : class, new() { var type = typeof(T); - var props = Helpers.GetExcelCustomPropertyInfos(type); + + var first = true; + List props = null; + var headers = Query(false, sheetName, configuration).FirstOrDefault()?.Values?.Select(s=>s?.ToString())?.ToArray(); //TODO:need to optimize foreach (var item in Query(true, sheetName, configuration)) { + if (first) + { + //TODO: alert don't duplicate column name + props = Helpers.GetExcelCustomPropertyInfos(type, headers); + first = false; + } var v = new T(); foreach (var pInfo in props) { + //TODO:don't need to check every time? if (item.ContainsKey(pInfo.ExcelColumnName)) { object newV = null; diff --git a/src/MiniExcel/Utils/Helpers.cs b/src/MiniExcel/Utils/Helpers.cs index bf889f1a..1345123e 100644 --- a/src/MiniExcel/Utils/Helpers.cs +++ b/src/MiniExcel/Utils/Helpers.cs @@ -69,11 +69,6 @@ internal static List GetSaveAsProperties(this Type type if (props.Count == 0) throw new InvalidOperationException($"{type.Name} un-ignore properties count can't be 0"); - // TODO: complex case like mix with columnindex and columnname - - // TODO: get header columns and index first - - // https://github.com/shps951023/MiniExcel/issues/142 //TODO: need optimize performance { @@ -111,14 +106,8 @@ internal static List GetSaveAsProperties(this Type type index++; } } - return newProps; } - - - - - } internal class ExcelCustomPropertyInfo @@ -130,7 +119,7 @@ internal class ExcelCustomPropertyInfo public bool Nullable { get; internal set; } } - internal static List GetExcelCustomPropertyInfos(Type type) + internal static List GetExcelCustomPropertyInfos(Type type, string[] headers) { List props = GetExcelPropertyInfo(type, BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) .Where(prop => prop.Property.IsSupportSetMethod() && !prop.Property.GetAttributeValue((ExcelIgnoreAttribute x) => x.ExcelIgnore)) @@ -139,6 +128,24 @@ internal static List GetExcelCustomPropertyInfos(Type t if (props.Count == 0) throw new InvalidOperationException($"{type.Name} un-ignore properties count can't be 0"); + { + var withCustomIndexProps = props.Where(w => w.ExcelColumnIndex != null && w.ExcelColumnIndex > -1); + if (withCustomIndexProps.GroupBy(g => g.ExcelColumnIndex).Any(_ => _.Count() > 1)) + throw new InvalidOperationException($"Duplicate column name"); + + foreach (var p in props) + { + if(p.ExcelColumnIndex != null) + { + if (p.ExcelColumnIndex >= headers.Length) + throw new ArgumentException($"ExcelColumnIndex {p.ExcelColumnIndex} over haeder max index {headers.Length}"); + p.ExcelColumnName = headers[(int)p.ExcelColumnIndex]; + if (p.ExcelColumnName == null) + throw new InvalidOperationException($"{p.Property.DeclaringType.Name} {p.Property.Name}'s ExcelColumnIndex {p.ExcelColumnIndex} can't find excel column name"); + } + } + } + return props; } diff --git a/tests/MiniExcelTests/MiniExcelIssueTests.cs b/tests/MiniExcelTests/MiniExcelIssueTests.cs index 72045c41..b185edb0 100644 --- a/tests/MiniExcelTests/MiniExcelIssueTests.cs +++ b/tests/MiniExcelTests/MiniExcelIssueTests.cs @@ -26,36 +26,63 @@ public void Issue142() { { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx"); - MiniExcel.SaveAs(path, new Issue142VO[] { new Issue142VO { } }); + MiniExcel.SaveAs(path, new Issue142VO[] { new Issue142VO { MyProperty1= "MyProperty1", MyProperty2= "MyProperty2", MyProperty3= "MyProperty3", MyProperty4= "MyProperty4", MyProperty5= "MyProperty5", MyProperty6= "MyProperty6", MyProperty7= "MyProperty7" } }); - var rows = MiniExcel.Query(path).ToList(); + { + var rows = MiniExcel.Query(path).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 rows = MiniExcel.Query(path).ToList(); - Assert.Equal("MyProperty4", rows[0].A); - Assert.Equal("CustomColumnName", rows[0].B); - 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(0, rows[1].A); - Assert.Equal(0, rows[1].B); - Assert.Equal(0, rows[1].C); - Assert.Equal(0, rows[1].D); - Assert.Equal(0, rows[1].E); - Assert.Equal(null, rows[1].F); - Assert.Equal(0, rows[1].G); + + 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); + } } { var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.csv"); - MiniExcel.SaveAs(path, new Issue142VO[] { new Issue142VO { } }); - + 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 -0,0,0,0,0,,0 +MyProperty4,MyProperty1,MyProperty5,MyProperty2,MyProperty6,,MyProperty3 "; Assert.Equal(expected, File.ReadAllText(path)); + + { + var rows = MiniExcel.Query(path).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); + } } { @@ -65,20 +92,58 @@ public void Issue142() } } + [Fact] + public void Issue142_Query() + { + { + var path = @"..\..\..\..\..\samples\xlsx\TestIssue142.xlsx"; + Assert.Throws(() => MiniExcel.Query(path).ToList()); + } + + { + var path = @"..\..\..\..\..\samples\xlsx\TestIssue142.xlsx"; + Assert.Throws(() => MiniExcel.Query(path).ToList()); + } + + { + var path = @"..\..\..\..\..\samples\xlsx\TestIssue142.xlsx"; + var rows = MiniExcel.Query(path).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 rows = MiniExcel.Query(path).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 int MyProperty1 { get; set; } //index = 1 + public string MyProperty1 { get; set; } //index = 1 [ExcelIgnore] - public int MyProperty7 { get; set; } //index = null - public int MyProperty2 { get; set; } //index = 3 + public string MyProperty7 { get; set; } //index = null + public string MyProperty2 { get; set; } //index = 3 [ExcelColumnIndex(6)] - public int MyProperty3 { get; set; } //index = 6 + public string MyProperty3 { get; set; } //index = 6 [ExcelColumnIndex("A")] // equal column index 0 - public int MyProperty4 { get; set; } + public string MyProperty4 { get; set; } [ExcelColumnIndex(2)] - public int MyProperty5 { get; set; } //index = 2 - public int MyProperty6 { get; set; } //index = 4 + public string MyProperty5 { get; set; } //index = 2 + public string MyProperty6 { get; set; } //index = 4 } public class Issue142VoDuplicateColumnName @@ -93,6 +158,18 @@ public class Issue142VoDuplicateColumnName 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 ///