2010-06-22 8 views
1

Je reçois une 'System.OutOfMemoryException' en essayant de générer des rapports HTML.Refactoring - System.OutOfMemoryException

Comment puis-je le factoriser de manière à ce que ceci soit tamponné dans un fichier, au lieu de le lire en mémoire, puis de l'écrire dans un fichier.

Il y aura plus de 2000 enregistrements dans le datatable, et il manque déjà de mémoire à 2000 lignes.

DétailsUpdateTemplate contient un extrait html multi-ligne. Je suppose que je crée une chaîne qui est trop grande.

J'utilise C#, .NET 3.5

internal static String SaveARSUpdateHTML(DataTable table, string fileName) 
    { 
     int recordCount = table.Rows.Count; 

     Dictionary<String, object> templateCols = new Dictionary<string, object>(); 

     templateCols["Track"] = table.TableName; 
     templateCols["ProdDate"] = DateTime.Now.ToShortDateString(); 
     templateCols["ProdTime"] = DateTime.Now.ToShortTimeString(); 
     templateCols["TotalRecords"] = recordCount; 

     String detailOutput = String.Empty; 
     for (int i = 0; i < table.Rows.Count; i++) 
     { 
      int ResultID = i + 1; 
      DataRow row = table.Rows[i]; 
      String ReportDetails = DetailsUpdateTemplate; 
      ReportDetails = ReportDetails.Replace(String.Format("{{{0}}}", "ResultID"), ResultID.ToString()); 
      foreach (DataColumn column in table.Columns) 
      { 
       String value = row[column.ColumnName].ToString(); 
       if (column.ColumnName.Equals("TF")) 
       { 
        String display = value.Equals("no", StringComparison.CurrentCultureIgnoreCase) ? "none" : "block"; 
        ReportDetails = ReportDetails.Replace(String.Format("{{{0}}}", "SuppressNewAddr"), display); 
       } 

       ReportDetails = ReportDetails.Replace(String.Format("{{{0}}}", column.ColumnName), value); 
      } 

      detailOutput += ReportDetails; 
     } 

     templateCols["ReportDetails"] = detailOutput; 

     String masterOut = MasterUpdateTemplate; 
     foreach (KeyValuePair<string, object> pair in templateCols) 
     { 

      masterOut = masterOut.Replace(String.Format("{{{0}}}", pair.Key), pair.Value.ToString()); 
     } 

     String outputFile = String.Format("{0}.htm", fileName); 
     using (StreamWriter sw = new StreamWriter(outputFile)) 
     { 
      sw.Write(masterOut); 
     } 

     return outputFile; 
    } 
+1

Vous n'avez pas fourni la partie pertinente de votre code. Tout ce que fait ce code est de modifier certaines données ** déjà ** en mémoire pour les sauvegarder dans un fichier. 'DataTable' est en mémoire. Donc, au lieu d'utiliser 'DataTable' en utilisant SqlDataReader et de diffuser directement dans le fichier. –

+0

On me transmet un DataTable d'une bibliothèque différente. Je ne peux pas changer le comportement d'entrée. –

Répondre

8

Utilisez StringBuilders lorsque concaténer des chaînes plus d'une poignée de fois.

Surtout ici:

detailOutput += ReportDetails; 

Utiliser aussi un DataReader. Le DataReader retournera les enregistrements qui supportent IDataRecord, qui a une interface similaire à DataRow.

Vous rencontrez OOM à peu près le même nombre de lignes que lorsque j'utilise DataTables et DataSets dans ASP.NET, où les processus de travail ont une mémoire limitée avant d'être recyclés en cas d'utilisation excessive de la mémoire. C'est pourquoi j'ai été agressivement passer à DataReaders où je peux.

Mise à jour:

Une solution DataReader ressemblerait à ... (TableReaders existent, mais il ne vous acheter quoi que ce soit en termes de conservation de la mémoire, vous obtenez juste une auriez interface plus semblable à DataReaders)

internal static String SaveARSUpdateHTML(DbDataReader myReader, string fileName) 
    { 

if (myReader.HasRows) 
    while (myReader.Read()) 
    { 
     object something = myReader["TF"]; 
    } 

else 
    Console.WriteLine("No rows returned."); 

myReader.Close(); 
+0

Matthew, je soupçonne une faute de frappe: "quand j'utilise DataTables et DataReaders" devrait être "... et DataAdapters"? –

+0

@Henk Holterman. Oui. Trop de choses préfixées avec des données. – MatthewMartin

+0

StringBuilder peut/doit être utilisé ici, non seulement pour la concaténation, mais aussi pour Replace() dans le cycle principal. Bien que les lecteurs/adaptateurs ne semblent pas pertinents pour ce code spécifique en question. –

3

Utilisez StringBuilder au lieu de chaîne

1

Outre le StringBuilder évident

Pourquoi ne pas diffuser ce fichier directement dans un fichier, traitez simplement une ligne à la fois, puis remplacez votre modèle par ligne. De cette façon, vous n'avez même pas besoin de conserver le rapport entier en mémoire.

Le code de remplacement sur un gabarit énorme sera un cochon de mémoire.