Excel催化剂开源第16波-VSTO开发之脱离传统COM交互以提升性能

在VSTO开发或其他COM技术开发过程中,甚至VBA也是,在和Excel交互中,难免会遇到性能瓶颈问题,COM技术的交互实在太慢,对大量数据读写等操作,耗时太长,容易卡用户界面以为是程序死机等等。

在专业程序猿开发群体,极少直接用COM的方式来访问Excel文件,都是以其他的非COM如最为流行的NPOI和EPPLUS类库的方式访问,并且是免费的可用于商业项目的。

在VSTO中调用NPOI和EPPLUS,又是怎样一翻境界呢?因Excel催化剂主要是为数据分析群体服务,其中大量的场景是面积大数据量的,它大量使用了此两大累库,实现一些数据读取和数据导出的功能以提升程序性能。

为何要同时使用两大开源类库?

两大类库,各有特色,NPOI支持xls和xlsx两种格式文档,但其API接口稍难用,和日常VBA接口方法很大差异,且调用时麻烦。而EPPLUS的话,只能用于xml结构的xlsx文档,因此实现的接口好用,若只针对xlsx版格式的文档读写,首选EPPLUS。

既然是插件,不能限制用户使用文档的格式,兼容于低版本格式文件时,只能同时用两大类库,在不损失开发效率上,同时兼顾对用户的文档多样性场景需求。最终牺牲了一些程序大小问题,但Excel催化剂已在前述文章中提及,将引用dll文件分离将不影响我们后续的升级功能时的升级性能,总体来说,还是可以取得很好的平衡性。

在Excel催化剂上的使用场景

数据导出场景

在前一波中提到,DataTable的数据导出Excel单元格区域上,我们已经提及到若数据量大,改用EPPLUS来导出,在EPPLUS上将数据导出到工作表,只需一个现成的已封装好的方法。具体代码如下:

 using (var p = new ExcelPackage(fi))
 {
 ExcelWorksheet sht = p.Workbook.Worksheets[shtName];
 sht.Cells[1, 1].LoadFromDataTable(outputTable, true, OfficeOpenXml.Table.TableStyles.Light19);
 sht.Tables[0].Name = tableName;
 p.Save();
 };

遍历工作薄工作表对象

若使用Excel的Com方法对工作薄进行打开,关闭,其性能极其低下,若用NPOI或EPPLUS 类库,速度就快出好几十倍,在多文件下有必要转换成此两种类库访问。在Excel催化剂的合并工作表/薄,拆分工作表/薄的场景中大量使用。

遍历单元格属性

同样的,若不是仅仅获取单元格上的数据,而是需要获取单元格的其他属性如字体颜色、填充颜色、字号、字体名称、样式等信息时,用COM访问的方式,也是有十分大的性能瓶颈问题。

在Excel单元格管理相关功能上,大量使用了EPPLUS类库作这些的遍历访问,性能十分优秀。

具体代码发以下:

 private static void FillCellInfosToNewSht(StyleActivateScope styleScope, string shtName, string selRangeAddress, FileInfo fileInfo, DataTable resultTable, Func isMatchCell)
 {
 using (ExcelPackage package = new ExcelPackage(fileInfo))
 {
 ExcelWorkbook wkb = package.Workbook;
 //先删除结果表
 if (wkb.Worksheets.Any(s => s.Name == newShtName))
 {
 wkb.Worksheets.Delete(wkb.Worksheets[newShtName]);
 }
 if (styleScope == StyleActivateScope.StyleWkb)
 {
 foreach (var sht in wkb.Worksheets)
 {
 foreach (var cell in sht.Cells[sht.Dimension.Address])
 {
 if (isMatchCell(cell))
 {
 AddRowToTable(resultTable, sht.Name, cell);
 }
 }
 }
 }
 else if (styleScope == StyleActivateScope.StyleSht)
 {
 var sht = wkb.Worksheets[shtName];
 foreach (var cell in sht.Cells[sht.Dimension.Address])
 {
 if (isMatchCell(cell))
 {
 AddRowToTable(resultTable, sht.Name, cell);
 }
 }
 }
 else
 {
 var sht = wkb.Worksheets[shtName];
 foreach (var cell in sht.Cells[selRangeAddress])
 {
 if (isMatchCell(cell))
 {
 AddRowToTable(resultTable, sht.Name, cell);
 }
 }
 }
 var newSht = wkb.Worksheets.Add(newShtName);
 newSht.Cells["A1"].LoadFromDataTable(resultTable, true, OfficeOpenXml.Table.TableStyles.Light1);
 package.Save();
 }
 }
 private static void AddRowToTable(DataTable resultTable, string shtName, ExcelRangeBase cell)
 {
 DataRow dr = resultTable.NewRow();
 dr[0] = shtName;
 string cellAddress = cell.Address;
 dr[1] = cellAddress;
 dr[2] = Regex.Match(cellAddress, @"d+").Value;
 dr[3] = Regex.Match(cellAddress, @"[A-Za-z]+").Value;
 var cellValue = cell.Value;
 dr[4] = cellValue;
 dr[5] = cellValue is string ? "文本" : "数字";
 string cellFormula = cell.Formula;
 dr[6] = cellFormula;
 dr[7] = cellFormula.Contains("!") ? true : false;
 dr[8] = cell.StyleName;
 resultTable.Rows.Add(dr);
 }

结语

表面上大家都一样的插件功能,Excel催化剂内核上可完全地不一样,实现的效果无论是性能上还是用户友好度体验上,都是和其他一般性VBA写出来的插件有很大的差距。就如同样一辆汽车,有吉利的和奔驰的,大家都叫汽车。

希望此篇分享的技术,日后能够带后整体VSTO开发插件的开发者们有技能的提升。

展开阅读全文

页面更新:2024-02-18

标签:催化剂   性能   遍历   插件   场景   接口   传统   文档   格式   功能   方式   文件   程序   方法   数据   数码   工作

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top