【问题标题】:.Net System.OutOfMemoryException filling a datatable.Net System.OutOfMemoryException 填充数据表
【发布时间】:2010-12-01 05:20:18
【问题描述】:

我需要从 .dbf 文件中提取数据并将其转换为 xml。我写了一个程序,它做得很好。但是现在我们遇到了非常大的 .dbf 文件——比如 2GB +。这段代码会在这些文件上引发 OutOfMemoryException。

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

问题是 - 如果我在我的计算机上通过 Visual Studio 在调试模式下针对同一个 2GB .dbf 文件运行相同的代码,则不会引发异常。这几乎就像 Visual Studio 管理内存的方式与应用程序单独管理的方式不同。

有没有办法解决内存问题?我尝试使用具有类似结果的 DataAdapter。我在 Visual Studio 中看到的这种行为是预期/设计的吗?

【问题讨论】:

    标签: .net vb.net dataset oledb dbf


    【解决方案1】:

    数据表在内存中,因此它在处理大文件时会失败或运行速度非常慢,具体取决于文件的大小。

    您需要使用 SqlDataReader 逐条读取数据记录,并使用 XmlWriter 来创建 XML 文件。

    类似这样的东西(代码未检查)

    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
    

    【讨论】:

      【解决方案2】:

      您绝对不能在内存中加载整个 2GB 的数据库。您需要分块加载和处理数据库记录

      要部分加载数据库,您可以在 SELECT 命令中使用例如 TOP 和 ROWNUM 子句。查看 SQL Server 的文档了解更多详细信息。

      【讨论】:

        【解决方案3】:

        如果要处理大文件,考虑DataReader而不填充DataSet;它不会将整个表加载到内存中,而是逐行加载。并且也使用 SqlDataAdapter.Fill() 代替这种方式,不要忘记 Dispose 。

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

        您实际上并不需要调用 .Close() 进行连接,因为它会在您调用 .Dispose() 时调用。

        PS:您没有关闭 Reader,可能这就是原因。是的,VS.NET 会比 GC 更快地关闭它。

        【讨论】:

          【解决方案4】:

          为什么不做一些简单的事情:

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

          这将一次只流式传输数据,转换为对象,然后另存为 xml。这几乎不会使用任何内存,并且应该非常快。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-07-25
            • 1970-01-01
            • 2010-09-08
            • 2012-09-17
            • 1970-01-01
            • 2015-06-20
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多