我用于生成 Excel 电子表格的混合 Excel Interop/EPPlus 应用程序在每次执行时运行速度逐渐变慢。
为了找出原因,我下载了 ANTS Memory Profiler 并运行了该应用程序。
ANTS MP 在其“加载到 CLR 中的程序集”详细信息中告诉我有 2 个动态生成的程序集。一个是在“InitializeExcelObjects()”方法中,这是有道理的,因为它创建了一个 Excel.Application 和相关对象。但是(据说?)加载程序集的第二种方法是:
private void WriteHeader(String shortName)
{
const int LOGO_FIRST_ROW = 1;
const int LOGO_LAST_ROW = 5;
var rowRngUnitName = _xlSheet.Range[_xlSheet.Cells[UNIT_NAME_ROW, 1], _xlSheet.Cells[UNIT_NAME_ROW, 3]];
rowRngUnitName.Merge(Type.Missing);
rowRngUnitName.Font.Bold = true;
rowRngUnitName.Font.Size = UNIT_NAME_FONT_SIZE;
rowRngUnitName.Value2 = shortName;
var rowRngRptTitle = _xlSheet.Range[_xlSheet.Cells[REPORT_TITLE_ROW, 1], _xlSheet.Cells[REPORT_TITLE_ROW, 5]];
rowRngRptTitle.Merge(Type.Missing);
rowRngRptTitle.Font.Size = REPORT_TITLE_ROW_FONT_SIZE;
rowRngRptTitle.Font.Bold = true;
rowRngRptTitle.Value2 = ProduceUsageByMonthLabel;
var rowRngRptAdvisory = _xlSheet.Range[_xlSheet.Cells[REPORT_ADVISORY_ROW, 1], _xlSheet.Cells[REPORT_ADVISORY_ROW, 4]];
rowRngRptAdvisory.Merge(Type.Missing);
rowRngRptAdvisory.Font.Size = REPORT_ADVISORY_ROW_FONT_SIZE;
rowRngRptAdvisory.Value2 = ProductsHiddenAdvisoryLabel;
// col 5A (ANNUAL_CONTRACT_PRODUCTS_ROW)
var rowRngContractProducts = _xlSheet.Range[
_xlSheet.Cells[ANNUAL_CONTRACT_PRODUCTS_ROW, ITEMDESC_COL],
_xlSheet.Cells[ANNUAL_CONTRACT_PRODUCTS_ROW, ITEMDESC_COL]];
rowRngContractProducts.Merge(Type.Missing);
rowRngContractProducts.Font.Size = ANNUAL_CONTRACT_PRODUCTS_LEGEND_FONT_SIZE;
rowRngContractProducts.Font.Bold = true;
rowRngContractProducts.HorizontalAlignment = XlHAlign.xlHAlignCenter;
rowRngContractProducts.VerticalAlignment = XlVAlign.xlVAlignCenter;
rowRngContractProducts.Value2 = AnnualContractProductsLabel;
Borders border = rowRngContractProducts.Borders;
border.Weight = XlBorderWeight.xlThin;
border.LineStyle = XlLineStyle.xlContinuous;
// Logo
var logoRange = _xlSheet.Range[
_xlSheet.Cells[LOGO_FIRST_ROW, _grandTotalsColumn], _xlSheet.Cells[LOGO_LAST_ROW, _grandTotalsColumn + 1]];
ReportRunnerConstsAndUtils.PlacePicture(_logo, logoRange);
}
...牵连的是这个:
var rowRngUnitName = _xlSheet.Range[_xlSheet.Cells[UNIT_NAME_ROW, 1], _xlSheet.Cells[UNIT_NAME_ROW, 3]];
...它没有做任何不寻常的事情(范围在代码中的所有位置都分配了,那么为什么这个被认为与其他不同并且是程序集的产生者)?
是因为这是第一次分配范围吗?完成后是否会动态加载某些程序集(仅一次)?
这是调用相关方法的代码:
public bool GenerateProduceUsageRpt()
{
const int SLEEPY_TIME = 10000;
const int MONTH_SPLIT_CUTOFF = 7;
try
{
InitializeExcelObjects();
_monthsInReport = ReportRunnerConstsAndUtils.GetMonthsInReport(_monthBegin, _monthEnd, _beginYearStr, _endYearStr);
_grandTotalsColumn = _monthsInReport + 3;
_grandTotalsColumnPivotTable = _grandTotalsColumn - 1;
_lastMonthColumn = _grandTotalsColumn - 1;
InitializeYearAndMonthVals();
try
{
System.Windows.Forms.Application.DoEvents();
if (_xlSheet != null)
{
_xlSheet.Name = ProduceUsageByMonthSheetName;
_xlPivotDataSheet.Name = ProduceUsageByMonthPivotTableSheetName;
_xlPivotTableSheet.Name = "PivotTable";
WriteHeader(_unit);
WriteColumnHeadings();
. . .
这是意料之中的(因为此时程序集是动态加载的),或者这可能与正在经历的逐渐恶化的性能有关?
更新
根据 Glenn Ferrie 的评论,这种方法可能很有趣:
// From Jürgen Tschandl at http://stackoverflow.com/questions/7413107/excel-image-in-a-cell
internal static void PlacePicture(Image picture, Range destination)
{
Worksheet ws = destination.Worksheet;
Clipboard.SetImage(picture);
ws.Paste(destination, false);
Pictures p = ws.Pictures(Missing.Value) as Pictures;
if (p != null)
{
Picture pic = p.Item(p.Count) as Picture;
ScalePicture(pic, (double)destination.Width, (double)destination.Height);
}
}