2009-09-24 5 views
3

J'ai besoin d'extraire des données d'un fichier .dbf et de le transformer en xml. J'ai écrit une routine qui fait très bien. Cependant, nous rencontrons maintenant de très gros fichiers .dbf - comme 2GB +. Et ce code lève une exception OutOfMemoryException sur ces fichiers..Net System.OutOfMemoryException remplissant une datatable

Public Function GetData() As DataTable 
    Dim dt As New DataTable(Name) 
    Dim sqlcommand As String= "Select * From MyTable" 
    Dim cn As New OleDbConnection(myconnectionstring) 

    Try 
     cn.Open() 
     Dim cmd As New OleDbCommand(sqlcommand, cn) 
     dt.Load(cmd.ExecuteReader()) 
    Catch ex As Exception 
     Throw ex 
    Finally 
     dt.Dispose() 
     cn.Close() 
     cn.Dispose() 
    End Try 
    Return dt 

La chose est - si je lance ce même code sur mon ordinateur via Visual Studio en mode débogage contre le même 2 Go .dbf fichier pas exception est levée. Il est presque comme Visual Studio gère la mémoire différemment alors l'application fait tout seul.

Est-il possible de contourner les problèmes de mémoire? J'ai essayé d'utiliser un DataAdapter avec des résultats similaires. Ce comportement que je vois avec Visual Studio est-il attendu/prévu?

Répondre

6

Une base de données est en mémoire, elle échouera donc sur les gros fichiers ou sera très lente en fonction de la taille du fichier.

Vous devez utiliser un SqlDataReader pour lire l'enregistrement de données par enregistrement et un XmlWriter pour créer votre fichier XML.

Quelque chose comme ça (Code non vérifié)

Public Sub WriteToXml(Dim xmlFileName As String, Dim connectionString) 
    Dim writer As XmlWriter 
    writer = XmlWriter.Create(xmlFileName) 
    Dim commandText As String= "Select * From MyTable" 
    Dim connection As New OleDbConnection(connectionString) 

    Try 
     connection.Open() 
     Dim command As New OleDbCommand(commandText, connection) 
     Dim reader As SqlDataReader 
     reader = myCommand.ExecuteReader() 

     While reader.Read()    
      write.WriteRaw("xml") 
     End While 
    Catch ex As Exception 
     Throw ex 
    Finally   
     connection.Close() 
     connection.Dispose() 
    End Try 
End Sub 
1

Si vous souhaitez traiter des fichiers volumineux, pensez à DataReader sans remplir DataSet; il ne chargera pas toute la table en mémoire, mais passera rangée par rangée. Et utilisez aussi SqlDataAdapter.Fill() au lieu de cela, n'oubliez pas de le mettre au rebut.

Dim conn As New SqlConnection(connection) 
Dim adapter As New SqlDataAdapter() 
adapter.SelectCommand = new SqlCommand(query, conn) 
adapter.Fill(dataset) 
adapter.Dispose() 
conn.Dispose() 

Vous n'avez pas vraiment besoin d'appeler .Close() pour la connexion, comme on l'appelle lorsque vous appelez .Dispose(). PS: Vous ne fermez pas Reader, c'est probablement la raison pour laquelle. Oui, VS.NET le ferme plus rapidement que GC.

4

Vous ne pouvez en aucun cas charger une base de données de 2 Go en mémoire. Vous devrez charger et traiter les enregistrements de la base de données en blocs

Pour effectuer le chargement partiel de la base de données, vous pouvez utiliser par exemple les clauses TOP et ROWNUM dans la commande SELECT. Regardez la documentation de SQL Server pour plus de détails.

1

Pourquoi ne pas faire quelque chose de simple comme:

using (var writer = CreateXmlWriter(fileName)) 
{ 
    while (reader.Read()) 
    { 
    var value = new ObjectFromDatabaseReader(reader); 
    value.WriteXml(writer); 
    } 
} 

Cela vient diffuser les données d'une ligne à la fois, se convertir à un objet puis sauvegardez en tant que xml. Cela n'utilisera pratiquement pas de mémoire du tout et devrait être très rapide.

Questions connexes