10 Комити b9bfe16d5f ... 553f4b1b2a

Аутор SHA1 Порука Датум
  Piotr Czajkowski 553f4b1b2a Cleanup пре 10 месеци
  Piotr Czajkowski a22f65a4dc Added ToObject_DateOnlyAsString пре 10 месеци
  Piotr Czajkowski 5ef833a4b3 Added ToObject_DateTimeAsString пре 10 месеци
  Piotr Czajkowski ccc9baa162 Added HandleEnum пре 10 месеци
  Piotr Czajkowski 7c2619e145 Added HandleGuid пре 10 месеци
  Piotr Czajkowski 9fcc40f009 Added info on 2.8.1 пре 11 месеци
  Piotr Czajkowski c4002192fc Cosmetics пре 11 месеци
  Piotr Czajkowski 3c334d6169 Added WriteWithStartFrom пре 11 месеци
  Piotr Czajkowski f24992cae3 Better version пре 11 месеци
  Piotr Czajkowski 95081f212c Let's be strict пре 11 месеци

+ 3 - 1
.github/workflows/dotnet.yml

@@ -11,6 +11,8 @@ on:
 
 jobs:
   build:
+    permissions:
+      contents: read
 
     runs-on: ubuntu-latest
 
@@ -19,7 +21,7 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v4
       with:
-        dotnet-version: 8.0.x
+        dotnet-version: 8.x
     - name: Restore dependencies
       run: dotnet restore
       working-directory: ExcelORM

+ 3 - 2
ExcelORM/ExcelORM/ExcelORM.csproj

@@ -4,7 +4,7 @@
         <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
-        <Version>2.8.0</Version>
+        <Version>2.9.0</Version>
         <PackageProjectUrl>https://git.liox.eu/pczajkowski/ExcelORM</PackageProjectUrl>
         <RepositoryUrl>https://github.com/pczajkowski/ExcelORM</RepositoryUrl>
         <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -14,7 +14,8 @@
 	<Authors>Piotr Czajkowski</Authors>
 	<Description>Simple library to read/write C# objects from/to Excel files. </Description>
 	<RepositoryType>GitHub</RepositoryType>
-	<PackageReleaseNotes>Handling more number types. Properly handling appending to and reading from empty file.</PackageReleaseNotes>
+	<PackageReleaseNotes>Ability to start writing from given row.
+Trying to handle dates saved as text without throwing.</PackageReleaseNotes>
     </PropertyGroup>
 
     <ItemGroup>

+ 13 - 9
ExcelORM/ExcelORM/ExcelWriter.cs

@@ -19,9 +19,9 @@ public class ExcelWriter : IDisposable
         xlWorkbook = workbook ?? throw new ArgumentNullException(nameof(workbook));
     }
 
-    private static int GenerateHeader(IXLWorksheet worksheet, PropertyInfo[] properties)
+    private static int GenerateHeader(IXLWorksheet worksheet, PropertyInfo[] properties, uint? startFrom = null)
     {
-        var rowIndex = 1;
+        var rowIndex = startFrom == null ? 1 : (int)startFrom.Value;
         var cellIndex = 1;
         foreach (var property in properties)
         {
@@ -77,19 +77,23 @@ public class ExcelWriter : IDisposable
         }
     }
 
-    private static void Write<T>(IEnumerable<T> values, IXLWorksheet worksheet, bool append, uint? headerRowIndex = null, uint? appendFrom = null) where T : class
+    private static void Write<T>(IEnumerable<T> values, IXLWorksheet worksheet, bool append, uint? headerRowIndex = null, uint? startFrom = null) where T : class
     {
         var properties = typeof(T).GetProperties();
         List<Mapping>? mapping = [];
 
         var lastRow = worksheet.LastRowUsed();
-        if (lastRow == null) append = false;
+        if (lastRow == null)
+        {
+            append = false;
+            lastRow = worksheet.FirstRow();
+        }
         
-        var rowIndex = (append, startFrom: appendFrom) switch
+        var rowIndex = (append, startFrom) switch
         { 
-            (true, not null) => (int)appendFrom,
+            (true, not null) => (int)startFrom,
             (true, null) => lastRow.RowNumber() + 1,
-            _ => GenerateHeader(worksheet, properties) 
+            _ => GenerateHeader(worksheet, properties, startFrom) 
         };
 
         if (append)
@@ -108,7 +112,7 @@ public class ExcelWriter : IDisposable
         }
     }
 
-    public void Write<T>(IEnumerable<T> values, string? worksheetName = null, bool append = false, uint? headerRowIndex = null, uint? appendFrom = null) where T : class
+    public void Write<T>(IEnumerable<T> values, string? worksheetName = null, bool append = false, uint? headerRowIndex = null, uint? startFrom = null) where T : class
     {
         var xlWorksheet = xlWorkbook.Worksheets.FirstOrDefault(x => x.Name.Equals(worksheetName, StringComparison.InvariantCultureIgnoreCase));
         
@@ -116,7 +120,7 @@ public class ExcelWriter : IDisposable
             xlWorkbook.AddWorksheet(worksheetName)
             : xlWorkbook.Worksheets.Count == 0 ? xlWorkbook.AddWorksheet() : xlWorkbook.Worksheets.First();
 
-        Write(values, xlWorksheet, append, headerRowIndex, appendFrom);
+        Write(values, xlWorksheet, append, headerRowIndex, startFrom);
     }
 
     public void SaveAs(string path, IExcelConverter? converter = null)

+ 28 - 16
ExcelORM/ExcelORM/TypeExtensions.cs

@@ -7,31 +7,43 @@ namespace ExcelORM;
 
 public static class TypeExtensions
 {
+    private static object? HandleGuid(XLCellValue value, PropertyInfo property)
+    {
+        if (Guid.TryParse(value.GetText(), out var guid))
+            return guid;
+
+        if (property.PropertyType == typeof(Guid?)) return null;
+        return Guid.Empty; 
+    }
+
+    private static object? HandleEnum(XLCellValue value, PropertyInfo property, Type? nullableUnderlyingType)
+    {
+        if (nullableUnderlyingType != null)
+        {
+            return Enum.TryParse(nullableUnderlyingType, value.GetText(), true, out var enumNullableValue)
+                ? enumNullableValue : null;
+        }
+        
+        return Enum.TryParse(property.PropertyType, value.GetText(), true, out var enumValue)
+            ? enumValue : Enum.GetValues(property.PropertyType).GetValue(0);
+    } 
+    
     private static object? GetAdditionalTypeFromText(XLCellValue value, PropertyInfo? property = null)
     {
         if (property == null) return value.GetText();
         
         if (property.PropertyType == typeof(Guid) || property.PropertyType == typeof(Guid?))
-        {
-            if (Guid.TryParse(value.GetText(), out var guid))
-                return guid;
+            return HandleGuid(value, property);
 
-            if (property.PropertyType == typeof(Guid?)) return null;
-            return Guid.Empty;
-        }
+        if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
+            return DateTime.TryParse(value.GetText(), out var dateTime) ? dateTime : default;
 
-        if (property.PropertyType.IsEnum)
-        {
-            return Enum.TryParse(property.PropertyType, value.GetText(), true, out var enumValue)
-                ? enumValue : Enum.GetValues(property.PropertyType).GetValue(0);
-        }
+        if (property.PropertyType == typeof(DateOnly) || property.PropertyType == typeof(DateOnly?))
+            return DateOnly.TryParse(value.GetText(), out var dateOnly) ? dateOnly : default;
 
         var nullableUnderlyingType = Nullable.GetUnderlyingType(property.PropertyType);
-        if (nullableUnderlyingType is { IsEnum: true })
-        {
-            return Enum.TryParse(nullableUnderlyingType, value.GetText(), true, out var enumValue)
-                ? enumValue : null;
-        }
+        if (property.PropertyType.IsEnum || (nullableUnderlyingType is { IsEnum: true }))
+            return HandleEnum(value, property, nullableUnderlyingType);
         
         return value.GetText(); 
     }

+ 0 - 3
ExcelORM/ExcelORMTests/ExcelORMTests.csproj

@@ -53,9 +53,6 @@
       <None Update="testFiles\withFormula.xlsx">
         <CopyToOutputDirectory>Always</CopyToOutputDirectory>
       </None>
-      <None Update="testFiles\badDate.xlsx">
-        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-      </None>
       <None Update="testFiles\forAppendWithRubbish.xlsx">
         <CopyToOutputDirectory>Always</CopyToOutputDirectory>
       </None>

+ 0 - 9
ExcelORM/ExcelORMTests/ReaderTests.cs

@@ -11,7 +11,6 @@ public class ReaderTests
     private const string MultipleSheetsFile = "testFiles/multipleSheets.xlsx";
     private const string DifferentTypesFile = "testFiles/differentTypes.xlsx";
     private const string WithFormulaFile = "testFiles/withFormula.xlsx";
-    private const string BadDate = "testFiles/badDate.xlsx";
     private const string AdditionalTypes = "testFiles/additionalTypes.xlsx";
     
     [Fact]
@@ -112,14 +111,6 @@ public class ReaderTests
         Assert.NotEmpty(results);
     }
     
-    [Fact]
-    public void BadDateThrows()
-    {
-        using var reader = new ExcelReader(BadDate);
-        var exception = Assert.Throws<ArgumentException>(() => reader.Read<TestTypes>().ToArray());
-        Assert.Contains("Location", exception.Message);
-    }
-    
     [Fact]
     public void ReadAdditionalTypes()
     {

+ 31 - 0
ExcelORM/ExcelORMTests/TypeExtensionsTests.cs

@@ -0,0 +1,31 @@
+using ClosedXML.Excel;
+using ExcelORM;
+
+namespace ExcelORMTests;
+
+public class TypeExtensionsTests
+{
+    public DateTime? DateTimeProperty { get; set; }
+    
+    [Fact]
+    public void ToObject_DateTimeAsString()
+    {
+        XLCellValue value = "7/27/2025";
+        
+        var propertyInfo = typeof(TypeExtensionsTests).GetProperty("DateTimeProperty");
+        var readValue = value.ToObject(propertyInfo);
+        Assert.IsType<DateTime>(readValue);
+    }
+    
+    public DateOnly? DateOnlyProperty { get; set; }
+    
+    [Fact]
+    public void ToObject_DateOnlyAsString()
+    {
+        XLCellValue value = "7/27/2025";
+        
+        var propertyInfo = typeof(TypeExtensionsTests).GetProperty("DateOnlyProperty");
+        var readValue = value.ToObject(propertyInfo);
+        Assert.IsType<DateOnly>(readValue);
+    }
+}

+ 20 - 1
ExcelORM/ExcelORMTests/WriterTests.cs

@@ -101,7 +101,7 @@ public class WriterTests
 
         uint headerRowIndex = 3;
         using var writer = new ExcelWriter(testFile);
-        writer.Write(ArrayOfThree, append: true, headerRowIndex: headerRowIndex, appendFrom: 7);
+        writer.Write(ArrayOfThree, append: true, headerRowIndex: headerRowIndex, startFrom: 7);
         writer.SaveAs(testFile);
 
         using var reader = new ExcelReader(testFile);
@@ -362,4 +362,23 @@ public class WriterTests
         for (int i = 0; i < readArray.Length; i++)
             Assert.Equal(ArrayOfThree[i], readArray[i]);
     }
+    
+    [Fact]
+    public void WriteWithStartFrom()
+    {
+        var testFile = Path.GetRandomFileName();
+        testFile = Path.ChangeExtension(testFile, "xlsx");
+
+        const string worksheetName = "Test";
+        using var writer = new ExcelWriter(testFile);
+        writer.Write(ArrayOfThree, worksheetName);
+        writer.Write(ListOfTwo, worksheetName, startFrom: (uint)(ArrayOfThree.Length + 3));
+        writer.SaveAs(testFile);
+
+        using var reader = new ExcelReader(testFile);
+        var readArray = reader.Read<Test>(worksheetName).ToArray();
+        Assert.Equal(6, readArray.Length);
+        
+        File.Delete(testFile);
+    }
 }

BIN
ExcelORM/ExcelORMTests/testFiles/badDate.xlsx


+ 2 - 1
versions.md

@@ -8,4 +8,5 @@
 | 2.5.0 | Added Location to ArgumentException Message in ExcelReader. It'll show address of affected cell and the worksheet's name.|
 | 2.6.0 | Added support for appending starting from given row.|
 | 2.7.0 | Added support for reading properties of type Guid and enum.|
-| 2.8.0 | Handling more number types. Properly handling appending to and reading from empty file.
+| 2.8.0 | Handling more number types. Properly handling appending to and reading from empty file.|
+| 2.8.1 | Ability to start writing from given row.|