Skip to content

開始使用 Excely

Max Zheng edited this page Nov 6, 2023 · 30 revisions

安裝

本套件尚未發布,暫時無法透過 Nuget 安裝。 請下載此專案並以專案參考形式或編譯為 .dll 檔以使用本套件。

開始使用

以下以 EPPlus.LGPL 依賴版本,進行 Excel 匯入、匯出的示範。

匯出

您所要做的第一件事是建立一個 Exporter,為此您需要引用 Excely.Workflows 命名空間。 Exporter 是我們為您包裝過後的類別,用以簡化匯出過程中的繁雜流程。 現在您可以透過 ExcelyExporter 快速建立一個 Exporter,比如以下所示:

var exporter = ExcelyExporter.FromClassList<Student>();

FromClassList<Student>() 代表我們要建立一個接收物件列表(準確來說是 IEnumerable)參數的匯出工具。
這個函式有許多參數,我們稍後再細看。現在,讓我們試著匯出一個基本的列表:

var students = new List<Student>()
{
    new Student(0, "Test1", DateTime.Now),
    new Student(1, "Test2", DateTime.Now),
};

var exporter = ExcelyExporter.FromClassList<Student>();
using var excel = exporter.ToExcel(students);

這樣就完成了一份 Excel 的匯出。

更深入一點

讓我們回頭看看 FromClassList 的參數:

  • propertyShowPolicy - 決定 Property 是否要成為匯出的欄位
  • propertyNamePolicy - 決定 Property 代表的欄位名稱(表頭顯示文字)
  • propertyOrderPolicy - 決定 property 排列的順序
  • customValuePolicy - 決定欄位寫入的值
  • shaders - 匯出後用於調整結果的執行單元

Policy 結尾的參數是以 Function 的方式傳入,用以決定匯出邏輯。他們都可以是 null,代表以預設行為匯出, 但接下來讓我們動點手腳...

假設我們想讓表頭改為中文,Student.Id 隱藏不匯出,名字排在生日的後面,日期改採 yyyy/MM/dd 格式,則可以這樣做:

var exporter = ExcelyExporter.FromClassList<Student>(
    propertyShowPolicy: p => p.Name != nameof(Student.Id),
    propertyNamePolicy: p => p.Name switch
    {
        nameof(Student.Name) => "名字",
        nameof(Student.Birthday) => "生日",
        _ => string.Empty
    },
    customValuePolicy: (student, p) => p.Name switch
    {
        nameof(Student.Birthday) => student.Birthday.ToString("yyyy/MM/dd"),
        _ => p.GetValue(student)
    });

這會需要您有些許映射與委派的基礎概念,搭配上 Lambdaswitch 運算式,可以非常容易的修改匯出邏輯。

至於 shaders 參數則可以傳入用於調整匯出結果的執行單元,比如可以為表頭加上篩選的 SchemaFilterShader、自適應儲存格大小的 CellFittingShader 等等。 使用方式如下:

var exporter = ExcelyExporter.FromClassList<Student>(
    shaders: new IShader[] {
        new CellFittingShader(),
        new SchemaFilterShader(3)
    });

更多關於 Shader 的資訊,請參考這裡(尚未撰寫)。

匯入

匯入與匯出類似,也需要建立 Importer,讓我們同樣引用 Excely.Workflows 命名空間。 建立 Importer 較為特別,需要分為兩段進行建立,第一次指定匯入來源,第二次指定目標結構。

var importer = new XlsxImporterBuilder().BuildForClassList<Student>();

透過建立 XlsxImporterBuilder,我們指定匯入來源為 Xlsx 檔案,接著呼叫 BuildForClassList<Student> 來指名我們想將資料匯入至 IEnumerable<Student>。 接著,我們將第一次匯出的 Excel 中的第一個工作表取出,用於當作匯入來源:

// 匯出為 Excel
var exporter = ExcelyExporter.FromClassList<Student>();
using var excel = exporter.ToExcel(students);

// 匯入為 List<Student>
var importer = new XlsxImporterBuilder().BuildForClassList<Student>();
var importResult = importer.Import(excel.Workbook.Worksheets.First());

這樣就完成了 Excel 的匯入。

更深入一點

我們同樣可以發現 BuildForClassList 中有許多參數可供選擇:

  • hasSchema - 指示匯入的資料是否含有表頭。若有,則使用 propertyNamePolicy,否則使用 propertyIndexPolicy
  • stopWhenError - 指示當匯入發生錯誤(通常是型別不符)時是否中斷匯入過程
  • propertyNamePolicy - 同匯出時,定義 Property 作為標頭的名稱。(這裡沒有反過來以表頭名稱獲得 Property 的原因是希望匯入、匯出能夠共用邏輯)
  • propertyIndexPolicy - 當資料沒有表頭時,以欄位順序解析對應的 Property
  • customValuePolicy - 決定如何將資料的值寫入至物件中
  • errorHandlingPolicy - 決定當錯誤發生時的處理方式,以及是否忽略此錯誤

參數使用方式與匯出時類似,特別要注意的是 propertyNamePolicypropertyIndexPolicy 不會同時被執行,而是根據 hasSchema 來決定呼叫哪一個邏輯。

接著來說說最複雜的 errorHandlingPolicy 參數,他需要傳遞5個引數,分別是
(錯誤儲存格座標, 正在寫入的物件, 正在解析的 Property, 儲存格中的值, 發生的錯誤)

需要特別注意的是 正在寫入的物件,由於解析正進行到一半,因次物件中的資料絕對不是完整的, 比如解析到第2個欄位時出錯,則第一個欄位對應的Property會有值,其餘欄位則為預設值。

最後,errorHandlingPolicy 需要回傳一個 bool,指名這次錯誤是否有被成功善後, 若回傳 true,則繼續進行下一個欄位的解析, 否則根據 stopWhenError,若為 true,則中斷匯入;若為 false,則忽略這一 Row 資料,開始解析下一 Row。

以下是一個將錯誤紀錄下來,並繼續匯入其他資料的範例:

var cellErrors = new Dictionary<CellLocation, string>();

var importer = new XlsxImporterBuilder().BuildForClassList<Student>(
    stopWhenError: false,
    errorHandlingPolicy: (cell, student, prop, value, ex) =>
    {
        cellErrors.Add(cell, ex.ToString());
        return true;
    });
var importResult = importer.Import(excel.Workbook.Worksheets.First());
Clone this wiki locally