WeihanLi.Npoi 1.11.0/1.12.0 Release Notes

WeihanLi發表於2020-11-05

WeihanLi.Npoi 1.11.0/1.12.0 Release Notes

Intro

最近 NPOI 擴充套件新更新了兩個版本,感謝 shaka chow 的幫忙和支援,這兩個 Feature 都是來自於他,並且幫我測試了幾個版本,還幫我提供了一個更好用的讀檔案的方式,十分感謝。

最近更新的兩個功能都是 Excel 匯入方面的,1.11.0 版本支援公式匯入處理,1.12.0 支援通過 Attribute 配置允許讀取的列範圍, 也可以通過 Fluent API 的方式設定 CellFilter 來更靈活的過濾不想讀取的列。

公式匯入的支援

在之前的版本中是沒有公式支援的,匯入一個公式的單元格會是一個字串去讀取,某些場景下可能用 Excel 會很有用,於是嘗試新增了公式的支援,僅是匯入,匯出的時候沒有支援公式,一方面是因為根據公式去計算匯出的值的時候可能會需要先把所有值先填完整再計算公式的值,這樣的場景會導致效率很低,另外一方面我覺得 Fluent API 的方式已經可以滿足大多數場景的需要,不是特別需要,所以沒有支援公式的匯出。

Row/Cell Filter 支援

在 1.10.0 版本中,我們支援了一個 EndRowIndex 來配置一個結束行,用以提前結束讀取資料,在 1.12.0 版本中 shaka 提出了可以增加 StartColumnIndex 以及 EndColumnIndex 配置來指定要讀取的列範圍,在此基礎上想出了基於 Fluent API 給 Sheet 增加 RowFilterCellFilter 來更靈活的配置自己想要讀取的資料範圍,藉助於此我們可以輕鬆實現隔行讀取或多個範圍讀取等。

Show the Code

Excel 公式匯入支援的示例:

[Theory]
[InlineData(ExcelFormat.Xls)]
[InlineData(ExcelFormat.Xlsx)]
public void ExcelImportWithFormula(ExcelFormat excelFormat)
{
    var setting = FluentSettings.For<ExcelFormulaTestModel>();
    setting.HasSheetConfiguration(0, "Test", 0);
    setting.Property(x => x.Num1).HasColumnIndex(0);
    setting.Property(x => x.Num2).HasColumnIndex(1);
    setting.Property(x => x.Sum).HasColumnIndex(2);

    var workbook = ExcelHelper.PrepareWorkbook(excelFormat);
    var sheet = workbook.CreateSheet();
    var row = sheet.CreateRow(0);
    row.CreateCell(0, CellType.Numeric).SetCellValue(1);
    row.CreateCell(1, CellType.Numeric).SetCellValue(2);
    row.CreateCell(2, CellType.Formula).SetCellFormula("$A1+$B1");
    var excelBytes = workbook.ToExcelBytes();
    var list = ExcelHelper.ToEntityList<ExcelFormulaTestModel>(excelBytes, excelFormat);
    Assert.NotNull(list);
    Assert.NotEmpty(list);
    Assert.Equal(1, list[0].Num1);
    Assert.Equal(2, list[0].Num2);
    Assert.Equal(3, list[0].Sum);
}

公式的支援不需要修改任何程式碼,和原來的 API 是完全相容的,可以看到上面公式的匯入的值成功被替換成了計算後的值

Cell Filter 使用 Attribute 方式示例

[Theory]
[InlineData(ExcelFormat.Xls)]
[InlineData(ExcelFormat.Xlsx)]
public void ExcelImportWithCellFilterAttributeTest(ExcelFormat excelFormat)
{
    IReadOnlyList<CellFilterAttributeTest> list = Enumerable.Range(0, 10).Select(i => new CellFilterAttributeTest()
    {
        Id = i + 1,
        Description = $"content_{i}",
        Name = $"title_{i}",
    }).ToArray();
    var excelBytes = list.ToExcelBytes(excelFormat);
    var importedList = ExcelHelper.ToEntityList<CellFilterAttributeTest>(excelBytes, excelFormat);
    Assert.NotNull(importedList);
    Assert.Equal(list.Count, importedList.Count);
    for (var i = 0; i < importedList.Count; i++)
    {
        Assert.Equal(list[i].Id, importedList[i].Id);
        Assert.Equal(list[i].Name, importedList[i].Name);
        Assert.Null(importedList[i].Description);
    }
}

[Sheet(SheetName = "test", AutoColumnWidthEnabled = true, StartColumnIndex = 0, EndColumnIndex = 1)]
private class CellFilterAttributeTest
{
    [Column(Index = 0)]
    public int Id { get; set; }

    [Column(Index = 1)]
    public string Name { get; set; }

    [Column(Index = 2)]
    public string Description { get; set; }
}

可以看到最後一列的值其實是被忽略掉的,最後一列對應的 Description 屬性永遠是 null

Cell Filter 使用 Fluent API 方式示例

[Theory]
[InlineData(ExcelFormat.Xls)]
[InlineData(ExcelFormat.Xlsx)]
public void ExcelImportWithCellFilter(ExcelFormat excelFormat)
{
    IReadOnlyList<Notice> list = Enumerable.Range(0, 10).Select(i => new Notice()
    {
        Id = i + 1,
        Content = $"content_{i}",
        Title = $"title_{i}",
        PublishedAt = DateTime.UtcNow.AddDays(-i),
        Publisher = $"publisher_{i}"
    }).ToArray();
    var excelBytes = list.ToExcelBytes(excelFormat);

    var settings = FluentSettings.For<Notice>();
    settings.HasSheetSetting(setting =>
    {
        setting.CellFilter = cell => cell.ColumnIndex == 0;
    });

    var importedList = ExcelHelper.ToEntityList<Notice>(excelBytes, excelFormat);
    Assert.Equal(list.Count, importedList.Count);
    for (var i = 0; i < list.Count; i++)
    {
        if (list[i] == null)
        {
            Assert.Null(importedList[i]);
        }
        else
        {
            Assert.Equal(list[i].Id, importedList[i].Id);
            Assert.Null(importedList[i].Title);
            Assert.Null(importedList[i].Content);
            Assert.Null(importedList[i].Publisher);
            Assert.Equal(default(DateTime).ToStandardTimeString(), importedList[i].PublishedAt.ToStandardTimeString());
        }
    }

    settings.HasSheetSetting(setting =>
    {
        setting.CellFilter = null;
    });
}

這個示例比較簡單,只匯入了第一列的資料 ,其他列資料對應的屬性都是預設值

More

除了這兩個主要的 Feature 之外,還有幾個小更新,重構了 ExcelSettingSheetSetting ,提供了基於委託來配置的方法,原來的方法作為擴充套件方法來使用,另外就是優化了檔案讀取,主要是讀取檔案的時候指定了一個 FileShare Mode(詳細可以參考文末給出的連結),原來如果別的程式已經開啟了檔案,這時候再匯入就會丟擲異常,優化之後即使檔案被別的程式佔用,依然可以讀取檔案內容進行匯入操作,操作體驗可能會更好一些。

更多細節可以參考 Github 倉庫裡的示例和單元測試

Reference

相關文章