天天看點

WeihanLi.Npoi 1.11.0/1.12.0 Release Notes

WeihanLi.Npoi 1.11.0/1.12.0 更新日志

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 增加

RowFilter

CellFilter

來更靈活的配置自己想要讀取的資料範圍,借助于此我們可以輕松實作隔行讀取或多個範圍讀取等。

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 之外,還有幾個小更新,重構了

ExcelSetting

SheetSetting

,提供了基于委托來配置的方法,原來的方法作為擴充方法來使用,另外就是優化了檔案讀取,主要是讀取檔案的時候指定了一個

FileShare

Mode(詳細可以參考文末給出的連結),原來如果别的程序已經打開了檔案,這時候再導入就會抛出異常,優化之後即使檔案被别的程序占用,依然可以讀取檔案内容進行導入操作,操作體驗可能會更好一些。

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

Reference

  • https://github.com/WeihanLi/WeihanLi.Npoi
  • https://www.nuget.org/packages/WeihanLi.Npoi/
  • https://github.com/WeihanLi/WeihanLi.Npoi/blob/dev/docs/ReleaseNotes.md
  • https://docs.microsoft.com/en-us/dotnet/api/system.io.fileshare?view=netcore-3.1#examples

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。