10 Commitit 9251af1699 ... 9a903598e9

Tekijä SHA1 Viesti Päivämäärä
  Piotr Czajkowski 9a903598e9 Be consistent 1 kuukausi sitten
  Piotr Czajkowski f35d26ec37 Cosmetics 1 kuukausi sitten
  Piotr Czajkowski 4c28eccadf More readable 1 kuukausi sitten
  Piotr Czajkowski 0085d30949 Info about version 2.6.0 1 kuukausi sitten
  Piotr Czajkowski a28fc8ab46 Version history as a link 1 kuukausi sitten
  Piotr Czajkowski 7d9c410ce6 Improved formatting 1 kuukausi sitten
  Piotr Czajkowski 94c171cb92 Added versions 1 kuukausi sitten
  Piotr Czajkowski 823e568433 Better version 1 kuukausi sitten
  Piotr Czajkowski ca7d40d323 Added WriteWithAppendExistingHeaderFirst 1 kuukausi sitten
  Piotr Czajkowski c9b6fc7e9d Added appendFrom 1 kuukausi sitten

+ 2 - 2
ExcelORM/ExcelORM/ExcelORM.csproj

@@ -4,7 +4,7 @@
         <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
-        <Version>2.5.0</Version>
+        <Version>2.6.0</Version>
         <PackageProjectUrl>https://git.liox.eu/pczajkowski/ExcelORM</PackageProjectUrl>
         <RepositoryUrl>https://github.com/pczajkowski/ExcelORM</RepositoryUrl>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -14,7 +14,7 @@
 	<Authors>Piotr Czajkowski</Authors>
 	<Description>Simple library to read/write C# objects from/to Excel files. </Description>
 	<RepositoryType>GitHub</RepositoryType>
-	<PackageReleaseNotes>Added Location to exception for easier troubleshooting.</PackageReleaseNotes>
+	<PackageReleaseNotes>Able to append starting from given row.</PackageReleaseNotes>
     </PropertyGroup>
 
     <ItemGroup>

+ 20 - 16
ExcelORM/ExcelORM/ExcelWriter.cs

@@ -38,15 +38,17 @@ public class ExcelWriter : IDisposable
     private static void WriteCell<T>(T value, PropertyInfo property, IXLCell cell)
     {
         var valueToSet = property.GetValue(value);
-        if (valueToSet == null) return;
-
-        if (valueToSet is SpecialBase specialProperty)
+        switch (valueToSet)
         {
-            specialProperty.SetCellValue(cell);
-            return;
+            case null:
+                return;
+            case SpecialBase specialProperty:
+                specialProperty.SetCellValue(cell);
+                return;
+            default:
+                cell.Value = XLCellValue.FromObject(valueToSet);
+                break;
         }
-
-        cell.Value = XLCellValue.FromObject(valueToSet);
     }
 
     private static void WriteRowAppend<T>(T value, IXLWorksheet worksheet, PropertyInfo[] properties, int rowIndex, List<Mapping> mapping)
@@ -75,22 +77,24 @@ public class ExcelWriter : IDisposable
         }
     }
 
-    private static void Write<T>(IEnumerable<T> values, IXLWorksheet worksheet, bool append, uint? headerRowIndex = null) where T : class
+    private static void Write<T>(IEnumerable<T> values, IXLWorksheet worksheet, bool append, uint? headerRowIndex = null, uint? appendFrom = null) where T : class
     {
-        if (!values.Any()) return;
-
-        int? rowIndex;
         var properties = typeof(T).GetProperties();
         List<Mapping>? mapping = [];
 
+        var rowIndex = (append, startFrom: appendFrom) switch
+        { 
+            (true, not null) => (int)appendFrom,
+            (true, null) => worksheet.LastRowUsed()?.RowNumber() + 1,
+            _ => GenerateHeader(worksheet, properties) 
+        };
+
         if (append)
         {
-            rowIndex = worksheet.LastRowUsed()?.RowNumber() + 1;
             var headerCells = headerRowIndex != null ? worksheet.Row((int)headerRowIndex).CellsUsed() : worksheet.FirstRowUsed()?.CellsUsed();
             mapping = Mapping.MapProperties<T>(headerCells);
             if (mapping == null || mapping.Count == 0) return;
-        } else
-            rowIndex = GenerateHeader(worksheet, properties);
+        }
 
         if (rowIndex == null) throw new NullReferenceException(nameof(rowIndex));
 
@@ -103,7 +107,7 @@ public class ExcelWriter : IDisposable
         }
     }
 
-    public void Write<T>(IEnumerable<T> values, string? worksheetName = null, bool append = false, uint? headerRowIndex = null) where T : class
+    public void Write<T>(IEnumerable<T> values, string? worksheetName = null, bool append = false, uint? headerRowIndex = null, uint? appendFrom = null) where T : class
     {
         var xlWorksheet = xlWorkbook.Worksheets.FirstOrDefault(x => x.Name.Equals(worksheetName, StringComparison.InvariantCultureIgnoreCase));
         
@@ -111,7 +115,7 @@ public class ExcelWriter : IDisposable
             xlWorkbook.AddWorksheet(worksheetName)
             : xlWorkbook.Worksheets.Count == 0 ? xlWorkbook.AddWorksheet() : xlWorkbook.Worksheets.First();
 
-        Write(values, xlWorksheet, append, headerRowIndex);
+        Write(values, xlWorksheet, append, headerRowIndex, appendFrom);
     }
 
     public void SaveAs(string path, IExcelConverter? converter = null)

+ 7 - 4
ExcelORM/ExcelORMTests/ExcelORMTests.csproj

@@ -10,13 +10,13 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
-        <PackageReference Include="xunit" Version="2.9.2" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
+        <PackageReference Include="xunit" Version="2.9.3" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="coverlet.collector" Version="6.0.2">
+        <PackageReference Include="coverlet.collector" Version="6.0.4">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
@@ -56,6 +56,9 @@
       <None Update="testFiles\badDate.xlsx">
         <CopyToOutputDirectory>Always</CopyToOutputDirectory>
       </None>
+      <None Update="testFiles\forAppendWithRubbish.xlsx">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+      </None>
     </ItemGroup>
 
     <ItemGroup>

+ 84 - 60
ExcelORM/ExcelORMTests/WriterTests.cs

@@ -6,18 +6,18 @@ namespace ExcelORMTests;
 
 public class WriterTests
 {
-    private static readonly Test[] arrayOfThree = 
-    {
+    private static readonly Test[] ArrayOfThree =
+    [
         new() { Name = "Bilbo", Surname = "Baggins", Job = "Eater"},
         new() { Name = "John", Job = "Policeman"},
         new() { Name = "Bruce", Surname = "Lee", Job = "Fighter"}
-    };
+    ];
 
-    private static readonly List<Test> listOfTwo = new()
-    {
-        new() { Name = "Elon", Surname = "Musk", Job = "Comedian"},
-        new() { Name = "Donald", Surname = "Trump", Job = "Bankrupt"},
-    };
+    private static readonly List<Test> ListOfTwo =
+    [
+        new() { Name = "Elon", Surname = "Musk", Job = "Comedian" },
+        new() { Name = "Donald", Surname = "Trump", Job = "Bankrupt" }
+    ];
 
     [Fact]
     public void WriteWithAppend()
@@ -27,7 +27,7 @@ public class WriterTests
 
         const string worksheetName = "Test";
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayOfThree, worksheetName);
+        writer.Write(ArrayOfThree, worksheetName);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
@@ -35,9 +35,9 @@ public class WriterTests
         Assert.Equal(3, readArray.Length);
 
         for (int i = 0; i < readArray.Length; i++)
-            Assert.Equal(arrayOfThree[i], readArray[i]);
+            Assert.Equal(ArrayOfThree[i], readArray[i]);
 
-        writer.Write(listOfTwo, worksheetName, true);
+        writer.Write(ListOfTwo, worksheetName, true);
         writer.SaveAs(testFile);
         
         using var newReader = new ExcelReader(testFile);
@@ -52,13 +52,13 @@ public class WriterTests
         testFile = Path.ChangeExtension(testFile, "xlsx");
 
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayOfThree);
+        writer.Write(ArrayOfThree);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
-        Assert.Equal(arrayOfThree.Length, reader.Read<Test>().Count());
+        Assert.Equal(ArrayOfThree.Length, reader.Read<Test>().Count());
 
-        writer.Write(listOfTwo, append: true);
+        writer.Write(ListOfTwo, append: true);
         writer.SaveAs(testFile);
         
         using var newReader = new ExcelReader(testFile);
@@ -77,19 +77,43 @@ public class WriterTests
 
         uint headerRowIndex = 3;
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayOfThree, append: true, headerRowIndex: headerRowIndex);
+        writer.Write(ArrayOfThree, append: true, headerRowIndex: headerRowIndex);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<Test>(startFrom: headerRowIndex).ToArray();
         Assert.Equal(6, readArray.Length);
 
-        for (int i = 0; i < arrayOfThree.Length; i++)
-            Assert.Equal(arrayOfThree[i], readArray[i+3]);
+        for (int i = 0; i < ArrayOfThree.Length; i++)
+            Assert.Equal(ArrayOfThree[i], readArray[i+3]);
 
         File.Delete(testFile);
     }
 
+    private const string ForAppendWithRubbish = "testFiles/forAppendWithRubbish.xlsx";
+    
+    [Fact]
+    public void WriteWithAppendExistingAndRubbish()
+    {
+        var testFile = Path.GetRandomFileName();
+        testFile = Path.ChangeExtension(testFile, "xlsx");
+        File.Copy(ForAppendWithRubbish, testFile);
+
+        uint headerRowIndex = 3;
+        using var writer = new ExcelWriter(testFile);
+        writer.Write(ArrayOfThree, append: true, headerRowIndex: headerRowIndex, appendFrom: 7);
+        writer.SaveAs(testFile);
+
+        using var reader = new ExcelReader(testFile);
+        var readArray = reader.Read<Test>(startFrom: headerRowIndex).ToArray();
+        Assert.Equal(6, readArray.Length);
+
+        for (int i = 0; i < ArrayOfThree.Length; i++)
+            Assert.Equal(ArrayOfThree[i], readArray[i+3]);
+
+        File.Delete(testFile);
+    }
+    
     private const string ForAppendHeaderFirst = "testFiles/forAppendHeaderFirst.xlsx";
 
     [Fact]
@@ -100,15 +124,15 @@ public class WriterTests
         File.Copy(ForAppendHeaderFirst, testFile);
 
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayOfThree, append: true);
+        writer.Write(ArrayOfThree, append: true);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<Test>().ToArray();
         Assert.Equal(6, readArray.Length);
 
-        for (int i = 0; i < arrayOfThree.Length; i++)
-            Assert.Equal(arrayOfThree[i], readArray[i + 3]);
+        for (int i = 0; i < ArrayOfThree.Length; i++)
+            Assert.Equal(ArrayOfThree[i], readArray[i + 3]);
 
         File.Delete(testFile);
     }
@@ -148,11 +172,11 @@ public class WriterTests
         File.Delete(testFile);
     }
 
-    private static readonly TestSkip[] arrayWithSkip =
-    {
+    private static readonly TestSkip[] ArrayWithSkip =
+    [
         new() {Text = "Lorem", Date = DateTime.Now.AddHours(1), Int = 1},
-        new() {Text = "Ipsum", Date = null, Int = 2},
-    };
+        new() {Text = "Ipsum", Date = null, Int = 2}
+    ];
 
     [Fact]
     public void WriteWithSkip()
@@ -162,28 +186,28 @@ public class WriterTests
 
         const string worksheetName = "Test";
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayWithSkip, worksheetName);
+        writer.Write(ArrayWithSkip, worksheetName);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<TestSkip>(worksheetName).ToArray();
-        Assert.Equal(arrayWithSkip.Length, readArray.Length);
+        Assert.Equal(ArrayWithSkip.Length, readArray.Length);
 
         for (int i = 0; i < readArray.Length; i++)
         {
-            Assert.NotEqual(arrayWithSkip[i].Text, readArray[i].Text);
-            Assert.Equal(arrayWithSkip[i].Date.ToString(), readArray[i].Date.ToString());
-            Assert.Equal(arrayWithSkip[i].Int, readArray[i].Int);
+            Assert.NotEqual(ArrayWithSkip[i].Text, readArray[i].Text);
+            Assert.Equal(ArrayWithSkip[i].Date.ToString(), readArray[i].Date.ToString());
+            Assert.Equal(ArrayWithSkip[i].Int, readArray[i].Int);
         }
 
         File.Delete(testFile);
     }
 
-    private static readonly TestSkipMiddle[] arrayWithSkipMiddle =
-    {
+    private static readonly TestSkipMiddle[] ArrayWithSkipMiddle =
+    [
         new() {Text = "Lorem", Date = DateTime.Now.AddHours(1), Int = 1},
-        new() {Text = "Ipsum", Date = DateTime.Now.AddHours(2), Int = 2},
-    };
+        new() {Text = "Ipsum", Date = DateTime.Now.AddHours(2), Int = 2}
+    ];
 
     [Fact]
     public void WriteWithSkipMiddle()
@@ -193,29 +217,29 @@ public class WriterTests
 
         const string worksheetName = "Test";
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayWithSkipMiddle, worksheetName);
+        writer.Write(ArrayWithSkipMiddle, worksheetName);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<TestSkipMiddle>(worksheetName).ToArray();
-        Assert.Equal(arrayWithSkipMiddle.Length, readArray.Length);
+        Assert.Equal(ArrayWithSkipMiddle.Length, readArray.Length);
 
         for (int i = 0; i < readArray.Length; i++)
         {
-            Assert.Equal(arrayWithSkipMiddle[i].Text, readArray[i].Text);
-            Assert.NotEqual(arrayWithSkipMiddle[i].Date.ToString(), readArray[i].Date.ToString());
-            Assert.Equal(arrayWithSkipMiddle[i].Int, readArray[i].Int);
+            Assert.Equal(ArrayWithSkipMiddle[i].Text, readArray[i].Text);
+            Assert.NotEqual(ArrayWithSkipMiddle[i].Date.ToString(), readArray[i].Date.ToString());
+            Assert.Equal(ArrayWithSkipMiddle[i].Int, readArray[i].Int);
         }
 
         File.Delete(testFile);
     }
 
-    private static readonly TestWithFormula[] arrayWithFormulas =
-    {
+    private static readonly TestWithFormula[] ArrayWithFormulas =
+    [
         new() { Name = "Bilbo", Surname = "Baggins", Job = "Eater", FullName = new Formula{ FormulaA1 = "B2&C2" } },
         new() { Name = "John", Job = "Policeman", FullName = new Formula{ FormulaA1 = "B3&C3" } },
-        new() { Name = "Bruce", Surname = "Lee", Job = "Fighter", FullName = new Formula{ FormulaA1 = "B4&C4" } },
-    };
+        new() { Name = "Bruce", Surname = "Lee", Job = "Fighter", FullName = new Formula{ FormulaA1 = "B4&C4" } }
+    ];
 
     [Fact]
     public void WriteWithFormula()
@@ -224,12 +248,12 @@ public class WriterTests
         testFile = Path.ChangeExtension(testFile, "xlsx");
 
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayWithFormulas);
+        writer.Write(ArrayWithFormulas);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<TestWithFormula>().ToArray();
-        Assert.Equal(arrayWithFormulas.Length, readArray.Length);
+        Assert.Equal(ArrayWithFormulas.Length, readArray.Length);
 
         foreach (var item in readArray)
         {
@@ -240,11 +264,11 @@ public class WriterTests
         File.Delete(testFile);
     }
 
-    private static readonly TestNumbersWithFormula[] arrayNumbersWithFormulas =
-    {
+    private static readonly TestNumbersWithFormula[] ArrayNumbersWithFormulas =
+    [
         new(){ First = 1, Second = 2, Sum = new Formula{FormulaA1 = "SUM(A2:B2)"} },
-        new(){ First = 2, Second = 3, Sum = new Formula{FormulaA1 = "SUM(A3:B3)"} },
-    };
+        new(){ First = 2, Second = 3, Sum = new Formula{FormulaA1 = "SUM(A3:B3)"} }
+    ];
 
     [Fact]
     public void NumbersWithFormula()
@@ -253,12 +277,12 @@ public class WriterTests
         testFile = Path.ChangeExtension(testFile, "xlsx");
 
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayNumbersWithFormulas);
+        writer.Write(ArrayNumbersWithFormulas);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<TestNumbersWithFormula>().ToArray();
-        Assert.Equal(arrayNumbersWithFormulas.Length, readArray.Length);
+        Assert.Equal(ArrayNumbersWithFormulas.Length, readArray.Length);
 
         foreach (var item in readArray)
         {
@@ -269,12 +293,12 @@ public class WriterTests
         File.Delete(testFile);
     }
 
-    private static readonly TestWithHyperlink[] arrayWithHyperlinks =
-    {
+    private static readonly TestWithHyperlink[] ArrayWithHyperlinks =
+    [
         new() { Name = "Bilbo", Surname = "Baggins", Job = "Eater", Link = new Hyperlink{ Value = "Wiki", Link = new XLHyperlink("https://en.wikipedia.org/wiki/Bilbo_Baggins") } },
         new() { Name = "John", Job = "Policeman", Link = new Hyperlink{ Value = "CNN", Link = new XLHyperlink("https://edition.cnn.com/2023/12/10/us/john-okeefe-boston-police-death-cec/index.html") } },
         new() { Name = "Bruce", Surname = "Lee", Job = "Fighter", Link = new Hyperlink{ Value = "IMDb", Link = new XLHyperlink("https://www.imdb.com/name/nm0000045/") } }
-    };
+    ];
 
     [Fact]
     public void WriteWithHyperlink()
@@ -283,19 +307,19 @@ public class WriterTests
         testFile = Path.ChangeExtension(testFile, "xlsx");
 
         using var writer = new ExcelWriter(testFile);
-        writer.Write(arrayWithHyperlinks);
+        writer.Write(ArrayWithHyperlinks);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
         var readArray = reader.Read<TestWithHyperlink>().ToArray();
-        Assert.Equal(arrayWithFormulas.Length, readArray.Length);
+        Assert.Equal(ArrayWithFormulas.Length, readArray.Length);
 
         for (var i = 0; i < readArray.Length; i++)
         {
-            Assert.NotNull(arrayWithHyperlinks[i].Link);
+            Assert.NotNull(ArrayWithHyperlinks[i].Link);
             Assert.NotNull(readArray[i].Link);
-            Assert.Equal(arrayWithHyperlinks[i].Link?.Value, readArray[i].Link?.Value);
-            Assert.Equal(arrayWithHyperlinks[i].Link?.Link?.ToString(), readArray[i].Link?.Link?.ToString());
+            Assert.Equal(ArrayWithHyperlinks[i].Link?.Value, readArray[i].Link?.Value);
+            Assert.Equal(ArrayWithHyperlinks[i].Link?.Link?.ToString(), readArray[i].Link?.Link?.ToString());
         }
         
         File.Delete(testFile);
@@ -307,13 +331,13 @@ public class WriterTests
         using var workbook = new XLWorkbook();
         const string worksheetName = "Test";
         using var writer = new ExcelWriter(workbook);
-        writer.Write(arrayOfThree, worksheetName);
+        writer.Write(ArrayOfThree, worksheetName);
 
         using var reader = new ExcelReader(workbook);
         var readArray = reader.Read<Test>(worksheetName).ToArray();
         Assert.Equal(3, readArray.Length);
 
         for (int i = 0; i < readArray.Length; i++)
-            Assert.Equal(arrayOfThree[i], readArray[i]);
+            Assert.Equal(ArrayOfThree[i], readArray[i]);
     }
 }

BIN
ExcelORM/ExcelORMTests/testFiles/forAppendWithRubbish.xlsx


+ 1 - 9
README.md

@@ -19,12 +19,4 @@ And their nullable variants.
 
 As always, feel free to use it however you desire. But I provide you with no guarantee whatsoever. Enjoy!
 
-## Version history
-In version 2 I've added ability to read data dynamically without a need to create a special type. Useful when you need to read/write some not so organized data.
-
-In version 2.2 I've added support for formulas, but as this library is based on ClosedXML it has its limitations, as per [Evaluating Formulas](https://github.com/closedxml/closedxml/wiki/Evaluating-Formulas):
-*Not all formulas are included and you'll probably get a nasty error if the formula isn't supported or if there's an error in the formula. Please test your formulas before going to production.*
-
-In version 2.3 I've added support for hyperlinks and improved appending to the existing files.
-
-In version 2.5 I've added Location to ArgumentException Message in ExcelReader. It'll show address of affected cell and the worksheet's name.
+## [Version history](versions.md)

+ 9 - 0
versions.md

@@ -0,0 +1,9 @@
+# Version history
+
+| Version | Description |
+| ----------- | ----------- |
+| 2.0.0 | I've added ability to read data dynamically without a need to create a special type. Useful when you need to read/write some not so organized data.|
+| 2.2.0 | I've added support for formulas, but as this library is based on ClosedXML it has its limitations, as per [Evaluating Formulas](https://github.com/closedxml/closedxml/wiki/Evaluating-Formulas): *Not all formulas are included and you'll probably get a nasty error if the formula isn't supported or if there's an error in the formula. Please test your formulas before going to production.*|
+| 2.3.0 | I've added support for hyperlinks and improved appending to the existing files.|
+| 2.5.0 | I've added Location to ArgumentException Message in ExcelReader. It'll show address of affected cell and the worksheet's name.|
+| 2.6.0 | I've added support for appending starting from given row.|