通过 SpreadSheetGear 读取 .xlsx 到 dto

Read .xlsx to dto via SpreadSheetGear

我想在 .Net Core 3.1 控制台应用程序中使用电子表格工具将 exel 文件读取到自定义模型(映射它!)。 我的 excel 文件如下所示:

| Customers | Sales Item | Sale Date  | Contact | Quantity |
| IBM       | Keyboard   | 28-10-2011 |         | 2        |
| Logitech  | Mouse      | 27-09-2011 | joe     | 5        |

DTO

public class CustomersDto
{
    public string Customers { get; set; }

    public string SalesItem { get; set; }

    public DateTime Date { get; set; }

    public string Contact  { get; set; }

    public int Quantity{ get; set; }
}

通用代码:

private static void Main(string[] args)
    {
        var workbook =
            Factory.GetWorkbook(
                @"D:\MyFiles\SellersCollections.xlsx");
        var worksheet = workbook.Worksheets[0];

        var cells = worksheet.UsedRange;
        List<CustomersDto> test = new List<CustomersDto>();
        CustomersDto dto = new CustomersDto();

        for (var i = 0; i < cells.RowCount; i++)
        {
            if (i == 1) // skip header (0)
            {
                for (var j = 0; j < cells.ColumnCount; j++)
                {
                   Console.WriteLine(cells[i,j].Value);

                   // What should I do HERE to MAP to DTO
                   // Something like:
                   // dto.Customers = cells[i, j]; ???
                }
            }
        }
        
        Console.ReadLine();
    }

是否有任何方法可以将 cells[i, j]IRange 映射到模型 class?

SpreadsheetGear 中不存在任何类型的内置映射或绑定功能来为您执行此操作,因此您需要构建自己的例程来执行此操作。如果您的工作簿和 DTO 定义明确且事先已知,我会保留外部“行循环”但删除内部“列循环”并手动索引每个列单元格值并将其放入相应的 DTO 属性 如所须。当然,这意味着您的代码对每一列中包含的信息及其对应的 属性 在 DTO 中的内容有所了解。如果您需要更动态的东西,则必须构建一个更复杂的系统,这超出了此处的回答范围。

就获取单元格值而言,您可能需要查看两个主要 IRange 属性:

  • IRange.Value - 此 属性 属于 object 类型,return 是单元格的“原始”值。因此,例如 1.23doubletruebool"abc"string
  • IRange.Text - this is of type string and returns the "formatted" value of a cell--meaning it takes IRange.Value and applies that cell's Number Format (IRange.NumberFormat) 到值和 returns 结果字符串。换句话说,这 return 是您从 Excel 的 UI 和/或 SpreadsheetGear 的 WorkbookView UI 控件中看到的内容。因此,如果一个单元格的 IRange.Value 为 double 1.23,但其格式设置为显示小数点后 4 位 (IRange.NumberFormat == "0.0000"),则 IRange.Text 将 return 字符串 "1.2300"。假设 booltrue 的单元格的通用 NumberFormat,IRange.Text 将 return 字符串 "TRUE"。等等。

需要特别考虑日期。这是因为 Excel 中的日期、时间和日期时间在内部存储为连续数字双精度值,值 1.0 为 1900 年 1 月 1 日,2.0 为 1900 年 1 月 2 日,今天(2021 年 3 月 23 日)为 44278, ETC。;该值的小数部分表示当天的时间(即 0.5 是中午,0.625 是 3:00 下午)。单元格显示为“日期”或“时间”或“日期时间”的事实纯粹是应用于单元格的 IRange.NumberFormat 的函数(“m/d/yyyy”或“h:mm:ss “, ETC)。如果您想从单元格中获取实际的 DateTime 对象,则需要使用 IWorkbook.NumberToDateTime(double serialDate) 辅助方法来执行此操作.

总而言之,您的例程可能如下所示:

private static void Main(string[] args)
{
    var workbook =
    Factory.GetWorkbook(
        @"D:\MyFiles\SellersCollections.xlsx");
    var worksheet = workbook.Worksheets[0];

    var cells = worksheet.UsedRange;
    List<CustomersDto> dtoList = new List<CustomersDto>();

    for (var i = 0; i < cells.RowCount; i++)
    {
        if (i == 1) // skip header (0)
        {
            CustomersDto dto = new CustomersDto();

            // First two columns appear to be text, using IRange.Text should be fine
            // but could probably use IRange.Value as well.
            dto.Customers = cells[i, 0].Text;
            dto.SalesItem = cells[i, 1].Text;

            // Need to convert serial date stored in cell values to actual DateTimes.
            // Use IRange.Value and cast to double (may need to introduce some value 
            // type checking here if other values or empty cell values are allowed.  
            // See IRange.ValueType to detect what kind of value is stored in a given 
            // cell).
            double serialDate = (double)cells[i, 2].Value;
            // Convert serial date to a true DateTime object
            DateTime dateTime = workbook.NumberToDateTime(serialDate);
            dto.Date = dateTime;

            // Contact column appears to be text, so IRange.Text should suffice.
            dto.Contact = cells[i, 3].Text;

            // Appears to be a number so cast to double (see note above about possible
            // value type checking) and then cast again to int.
            dto.Quantity = (int)(double)cells[i, 4].Value;

            // Add to list
            dtoList.Add(dto);
        }
    }

    Console.ReadLine();
}