2012-05-10 26 views
7

Estoy intentando escribir un archivo Excel desde un servidor web ASP.NET usando OpenXML. Tengo alrededor de 2100 registros y tomar alrededor de 20-30 segundos para hacer esto. ¿De alguna manera puedo hacerlo más rápido? Recuperar las 2100 filas del db lleva una fracción de segundo. No estoy seguro de por qué la manipulación de ellos en la memoria tomaría más tiempo..NET Problemas de rendimiento OpenXML

Nota: ExcelWriter es nuestra clase personalizada, pero todos sus métodos son directamente de código en este enlace, http://msdn.microsoft.com/en-us/library/cc861607.aspx

public static MemoryStream CreateThingReport(List<Thing> things, MemoryStream template) 
    { 
     SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(template, true); 
     WorksheetPart workSheetPart = spreadsheet.WorkbookPart.WorksheetParts.First(); 

     SharedStringTablePart sharedStringPart = spreadsheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First(); 

     Cell cell = null; 
     int index = 0; 

     //create cell formatting for header text 
     Alignment wrappedAlignment = new Alignment { WrapText = true }; 
       uint rowOffset = 2; 

    foreach (Thing t in things) 
     { 
      //Received Date 
      cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart); 
      index = ExcelWriter.InsertSharedStringItem(t.CreateDate.ToShortDateString(), sharedStringPart); 
      cell.CellValue = new CellValue(index.ToString()); 
      cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString); 

      //Car Part Name 
      cell = ExcelWriter.InsertCellIntoWorksheet("B", rowOffset, workSheetPart); 
      index = ExcelWriter.InsertSharedStringItem(t.CarPart.Name, sharedStringPart); 
      cell.CellValue = new CellValue(index.ToString()); 
      cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString); 

    rowOffset++; 
    } 

workSheetPart.Worksheet.Save(); 

     spreadsheet.WorkbookPart.Workbook.Save(); 
     spreadsheet.Close(); 

     return template; 

Respuesta

7

lo que parece que alguien en la documentación de la comunidad MSDN se encontró con implicaciones de rendimiento similares. El código a continuación es muy ineficiente. Alguien recomendó usar una tabla hash.

Para nuestra solución, simplemente eliminamos la inserción de cadenas compartidas en total y pasamos de 1:03 segundos a 0:03 segundos en el tiempo de descarga.

//Old: (1:03) 
      cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart); 
      index = ExcelWriter.InsertSharedStringItem(thing.CreateDate.ToShortDateString(), sharedStringPart); 
      cell.CellValue = new CellValue(index.ToString()); 
      cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString); 

//New: (0:03) 
      cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart); 
      cell.CellValue = new CellValue(thing.CreateDate.ToShortDateString()); 
       cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.String); 

MSDN Docs (solución lenta, se debe usar una tabla hash lugar)

 private static int InsertSharedStringItem(string text, SharedStringTablePart   shareStringPart) 
    { 
// If the part does not contain a SharedStringTable, create one. 
if (shareStringPart.SharedStringTable == null) 
{ 
    shareStringPart.SharedStringTable = new SharedStringTable(); 
} 

int i = 0; 

// Iterate through all the items in the SharedStringTable. If the text already exists, return its index. 
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>()) 
{ 
    if (item.InnerText == text) 
    { 
     return i; 
    } 

    i++; 
} 

// The text does not exist in the part. Create the SharedStringItem and return its index. 
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text))); 
shareStringPart.SharedStringTable.Save(); 

return i; 
} 
+0

Me enfrenta el mismo problema ... Tengo que escribir más de 1000 filas y en algunos casos más de 10000 filas y se está volviendo lento como el infierno ... Aquí dijiste que puedes usar tabla hash ¿puedes dar un ejemplo de cómo? o si alguna otra cosa usaste para mejorar el rendimiento ... – kunjee

+0

Estoy viendo 500,000 filas. ¿Has hecho otras mejoras desde esta publicación que podrías compartir? Me cambié al método SAX para minimizar el uso de la memoria. Y veo aproximadamente 1000 filas por ~ 1.1 segundos. Si te vuelves más rápido que esto, comparte. – CaptainBli

2

@kunjee

Si desea crear el rendimiento de todos los objetos requeridos por adelantado por lo que no se verifica en cada invocación de este método. Esta es la razón por la cual SharedStringTable se pasa como parámetro en lugar de la parte.

Los diccionarios son para una búsqueda indexada rápida, tienen un mejor rendimiento que un bucle for. Son un poco más rápidos que las tablas hash porque están fuertemente tipados, por lo que no requieren el uso del boxeo. Ser fuertemente tipado es un gran beneficio de todos modos.

private static int InsertSharedStringItem(string sharedString, SharedStringTable sharedStringTable, Dictionary<string, int> sharedStrings) 
{ 
    int sharedStringIndex; 

    if (!sharedStrings.TryGetValue(sharedString, out sharedStringIndex)) 
    { 
     // The text does not exist in the part. Create the SharedStringItem now. 
     sharedStringTable.AppendChild(new SharedStringItem(new Text(sharedString))); 

     sharedStringIndex = sharedStrings.Count; 

     sharedStrings.Add(sharedString, sharedStringIndex); 
    } 

    return sharedStringIndex; 
} 
4

@The Internet

Tenga en cuenta que tipo de datos String es en realidad para las fórmulas, para el texto debe utilizar InlineString. Ver 17.18.11 ST_CellType (tipo de célula):

  • inlineStr (Inline String) - Móvil que contiene una (en línea) rica cadena, es decir, que no está en la tabla de cadenas compartidas. Si se utiliza este tipo de celda, , entonces el valor de la celda está en el elemento is en lugar del elemento v en la celda (elemento c).
  • str (String) - Celda que contiene una cadena de fórmula.
2

La gran improment es más función Save() de bucle

//Save data 
     shareStringPart.SharedStringTable.Save(); 
     worksheetPart.Worksheet.Save(); 

Para 500 registros, para mí cambio de 10 minutos a 1 min.

+0

Esta es una parte muy importante del trabajo con OpenXML, ya que muchas de las subpartes requieren un guardado, tendemos a guardar dentro de los métodos de acción singular o bucles en lugar de en el ámbito externo donde el guardado es más eficiente. –

Cuestiones relacionadas