简体   繁体   中英

Improve OpenXML performance when inserting rows into Excel

I use OpenXML v 2.8.1.0 to create an excel report for a web site. Spreadsheet has 22 columns. Takes 30 seconds to render 15,000 rows, but that's too long for a web page. Can I do this more efficiently?

Code snippet is below (left out parts not germane to the issue). Suspect I'm inefficient in all that looping through each cell to set its cellreference, and then setting each row's rowIndex as well.

I left out code where I open the spreadsheet I am populating - it contains headers and a sample detail row, which has all formatting I want but all values are blank.

In code I clone that row, set values and cellreference for each cell, set rowIndex for the row, insert the row, then get the next row of data from the database and repeat.

Or is 500 rows per second about as fast as OpenXML is capable of?

Thanks.

public class MyReport
{
    public byte[] Get(List<dbRow> dbRows)
    {
        // dbRows is the result of "Select col0, col1, ... col21 from dbTable"

        // first 6 rows are the report headers
        // row 7 is the blank detail row.
        Row templateDetailRow = sheetData.Elements<Row>().Where(r => r.RowIndex == 7).First();
        // start inserting data rows at row number 8
        uint currentRowPointer = sheetData.Elements<Row>().LastOrDefault().RowIndex + 1; // start adding rows after the current last row
        Row currentBottomRow = sheetData.Elements<Row>().LastOrDefault(); // initialize to last row before we start adding rows

        foreach (dbRow r in dbRows)
        {
            Row newRow = (Row)templateDetailRow.Clone();
            List<Cell> cells = newRow.Elements<Cell>().OrderBy(c => c.CellReference.Value).ToList();
            SetCellsInRow(cells, r, currentRowPointer);
            newRow.RowIndex = currentRowPointer;
            InsertRow(sheetData, newRow);
            currentRowPointer++;
        }

    }
    private void SetCellsInRow(List<Cell> cells, dbRow source, uint rowIndex)
    {
        SetCell(cells[0], source.string0, rowIndex);
        SetCell(cells[1], source.int1, rowIndex);
        SetCell(cells[2], source.string2, rowIndex);
        SetCell(cells[3], source.string3, rowIndex);
        SetCell(cells[4], source.string4, rowIndex);
        SetCell(cells[5], String.Format("{0:MM/dd/yyyy}", source.date5), rowIndex);
        SetCell(cells[6], source.string6, rowIndex);
        SetCell(cells[7], source.int7, rowIndex);
        SetCell(cells[8], source.string8, rowIndex);
        SetCell(cells[9], source.string9, rowIndex);
        SetCell(cells[10], source.date10.ToString("MM/dd/yyyy"), rowIndex);
        SetCell(cells[11], String.Format("{0:MM/dd/yyyy}", source.date11), rowIndex);
        SetCell(cells[12], String.Format("{0:MM/dd/yyyy}", source.date12), rowIndex);
        SetCell(cells[13], source.string13, rowIndex);
        SetCell(cells[14], String.Format("{0:MM/dd/yyyy}", source.date14), rowIndex);
        SetCell(cells[15], source.int15 ?? 0, rowIndex);
        SetCell(cells[16], String.Format("{0:MM/dd/yyyy}", source.date16), rowIndex);
        SetCell(cells[17], source.string17, rowIndex);
        SetCell(cells[18], source.string18, rowIndex);
        SetCell(cells[19], source.int19, rowIndex);
        SetCell(cells[20], source.string20, rowIndex);
        SetCell(cells[21], source.string21, rowIndex);
    }

    private void SetCell(Cell theCell, string val, uint rowIndex)
    {   // a NON-SHARED string is going in to the cell
        theCell.CellValue = new CellValue(val);
        theCell.DataType = new EnumValue<CellValues>(CellValues.String);
        theCell.CellReference = Regex.Replace(theCell.CellReference, @"\d+", rowIndex.ToString());
    }

    private void SetCell(Cell theCell, int val, uint rowIndex)
    {    // an int is going in to the cell
        theCell.CellValue = new CellValue(val.ToString());
        theCell.DataType = new EnumValue<CellValues>(CellValues.Number);
        theCell.CellReference = Regex.Replace(theCell.CellReference, @"\d+", rowIndex.ToString());
    }

    private static void InsertRow(SheetData sheetData, Row row)
    {
        sheetData.InsertAfter(row, currentBottomRow);
        currentBottomRow = row;
    }
}

Received a great answer to same question posted on social.msdn:

It's much faster to forget trying to set rowIndex and CellReference. Instead leave them null, and use AppendChild. Here's my improved code:

public class MyReport
{
    public byte[] Get(List<dbRow> dbRows)
    {
        // dbRows is the result of "Select col0, col1, ... col21 from dbTable"


        foreach (dbRow r in dbRows)
        {
            Row newRow = new Row();

            SetCell(r.string0, newRow);
            SetCell(r.int1, newRow);
            SetCell(r.string2, newRow);
            SetCell(r.string3, newRow);
            SetCell(r.string4, newRow);
            SetCell(String.Format("{0:MM/dd/yyyy}", r.date5), newRow);
            SetCell(r.string6, rowIndex);
            SetCell(r.int7, rowIndex);
            SetCell(r.string8, rowIndex);
            SetCell(r.string9, rowIndex);
            SetCell(r.date10.ToString("MM/dd/yyyy"), rowIndex);
            SetCell(String.Format("{0:MM/dd/yyyy}", r.date11), rowIndex);
            SetCell(String.Format("{0:MM/dd/yyyy}", r.date12), rowIndex);
            SetCell(r.string13, rowIndex);
            SetCell(String.Format("{0:MM/dd/yyyy}", r.date14), rowIndex);
            SetCell(r.int15 ?? 0, rowIndex);
            SetCell(String.Format("{0:MM/dd/yyyy}", r.date16), rowIndex);
            SetCell(r.string17, rowIndex);
            SetCell(r.string18, rowIndex);
            SetCell(r.int19, rowIndex);
            SetCell(r.string20, rowIndex);
            SetCell(r.string21, rowIndex);

            sheetData.AppendChild(newRow);
        }

        private void SetCell(string val, Row theRow)
        {   // a NON-SHARED string is going in to the cell
            Cell theCell = new Cell();
            theCell.CellValue = new CellValue(val);
            theCell.DataType = new EnumValue<CellValues>(CellValues.String);
            theRow.AppendChild(theCell);
        }

        protected void SetCell(int val, Row theRow)
        {    // an int is going in to the cell
            Cell theCell = new Cell();
            theCell.CellValue = new CellValue(val.ToString());
            theCell.DataType = new EnumValue<CellValues>(CellValues.Number);
            theRow.AppendChild(theCell);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM