提高将大型execl文件转换为对象列表的性能

时间:2016-07-04 08:27:41

标签: c# excel performance epplus

我知道这个问题已被多次询问过。但我找不到任何人的帮助。

我不想将excel转换为数据表,但我希望将其转换为对象列表并发送到服务器端进行处理。

如果它有超过2K行,则应该抛出错误。目前我正在做的是:

   using (var excel = new ExcelPackage(hpf.InputStream))
    {
        var ws = excel.Workbook.Worksheets["Sheet1"];

        for (int rw = 4; rw <= ws.Dimension.End.Row; rw++)
        {
            if (ws.Cells[rw, 1].Value != null)
            {
                int headerRow = 2;

                GroupMembershipUploadInput gm = new GroupMembershipUploadInput();

                for (int col = ws.Dimension.Start.Column; col <= ws.Dimension.End.Column; col++)
                {
                    var s = ws.Cells[rw, col].Value;

                    if (ws.Cells[headerRow, col].Value.ToString().Equals("Existing Constituent Master Id"))
                    {
                        gm.cnst_mstr_id = (ws.Cells[rw, col].Value ?? (Object)"").ToString();
                    }
                    else if (ws.Cells[headerRow, col].Value.ToString().Equals("Prefix of the constituent(Mr, Mrs etc)"))
                    {
                        gm.cnst_prefix_nm = (ws.Cells[rw, col].Value ?? (Object)"").ToString();
                    }
                    else if (ws.Cells[headerRow, col].Value.ToString().Equals("First Name of the constituent(Mike)"))
                    {
                        gm.cnst_first_nm = (ws.Cells[rw, col].Value ?? (Object)"").ToString();
                    }
                    .....................
                    .....................


                    }
            }

                    iUploadedCnt = iUploadedCnt + 1; //Increase the count by 1
                }

                if (lgl.GroupMembershipUploadInputList.Count < 2003) //Check for the uploaded list count
                {

                       //throw the error

                 }

但这种做法似乎很慢。

将excel转换为列表对我来说似乎很慢。例如,当我上传超过2k条记录时,列表首先转换为列表,然后检查计数是否超过2003。这个过程肯定比较慢。

如何以更快/更好的方式实现目标?

1 个答案:

答案 0 :(得分:0)

你做了很多重复的字符串处理,这是不必要的。对于每一行,如果它们符合某个预定义值,则再次检查列标题。 (例如if (ws.Cells[headerRow, col].Value.ToString().Equals("Existing Constituent Master Id"))

您可以在开始解析所有行之前执行此操作,并创建一个Dictionary<int, SomeEnum>,将列号映射到特定的枚举值。解析行时,您可以在字典中快速查找,该列映射到哪个属性。

此外,您定义了var s = ws.Cells[rw, col].Value;但从不使用它。相反,当您将其分配给对象的属性时,会再次读取此单元格值。您可以在此处进行必要的转换和检查,然后仅使用s;

// define this enum somewhere
enum ColumPropEnum {
   cnst_mstr_id,  cnst_prefix_nm, ...
}

//define this prop somewhere
Dictionary<int, ColumnPropEnum> colprops = new Dictionary<int, ColumnPropEnum>();

//do this once before processing all rows
for (int col = ws.Dimension.Start.Column; col <= ws.Dimension.End.Column; col++) {
    if (ws.Cells[headerRow, col].Value.ToString().Equals("Existing Constituent Master Id")) 
        colprops.Add(col, ColumnPropEnum.cnst_mstr_id);
    else if (ws.Cells[headerRow, col].Value.ToString().Equals(" ..."))
        colprops.Add(col, ColumnPropEnum.cnst_prefix_nm);
    ...
}


//now use this dictionary in each row    
for (int rw = 4; rw <= ws.Dimension.End.Row; rw++)
{
....
    for (int col = ws.Dimension.Start.Column; col <= ws.Dimension.End.Column; col++) {

        //the single ? checks, whether the Value is null, if yes it returns null, otherwise it returns ToString(). Then the double ?? checks whether the result if the operation is null, if yes, it assigns "" to s, otherwise the result of ToString(); 
        var s = ws.Cells[rw, col].Value?.ToString() ?? "";
        ColumnPropEnum cp;
        if (colpros.TryGetValue(col, out cp)) {
            switch (cp) {
                case cnst_mstr_id: gm.cnst_mstr_id = s; break;
                case cnst_prefix_nm: gm.cnst_prefix_nm = s; break;
                ...
            }
        }
    }

}

我不确定您将此对象添加到列表中的位置或将其上载到服务器,因为这不是代码的一部分。但它可能更快,如果你有必要的非空值计数,首先只检查每一行的第一列,如果没有,则抛出一个错误,只有当你没有时才进行所有其他处理不要抛出错误。

int rowcount = 0;
//If you need at minimum 2000 rows, you can stop after you count 2000 valid rows
for (int rw = 4; rw <= ws.Dimension.End.Row && rowcount < 2000; rw++)        
{
    if (ws.Cells[rw, 1].Value != null) rowcount++
}

if (rowcount < 2000) {
    //throw error and return
} 

//else do the list building and uploading