10 次代碼提交 b9bfe16d5f ... 553f4b1b2a

作者 SHA1 備註 提交日期
  Piotr Czajkowski 553f4b1b2a Cleanup 1 周之前
  Piotr Czajkowski a22f65a4dc Added ToObject_DateOnlyAsString 1 周之前
  Piotr Czajkowski 5ef833a4b3 Added ToObject_DateTimeAsString 1 周之前
  Piotr Czajkowski ccc9baa162 Added HandleEnum 1 周之前
  Piotr Czajkowski 7c2619e145 Added HandleGuid 1 周之前
  Piotr Czajkowski 9fcc40f009 Added info on 2.8.1 2 周之前
  Piotr Czajkowski c4002192fc Cosmetics 2 周之前
  Piotr Czajkowski 3c334d6169 Added WriteWithStartFrom 2 周之前
  Piotr Czajkowski f24992cae3 Better version 3 周之前
  Piotr Czajkowski 95081f212c Let's be strict 3 周之前

+ 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);
+    }
 }

二進制
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.|