System.outOfMemory bei SQLDataAdapter.Fill()

  • VB.NET
  • .NET (FX) 4.0

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Fakiz.

    System.outOfMemory bei SQLDataAdapter.Fill()

    Hallo zusammen,

    ich stehe aktuell auf dem Schlauch. Der Punkt ist folgender. In einer DB auf einem SQLServer wird eine Abfrage per SQLCommand abgefeuert. Es soll eine Datei mit mindestens 500 MB Speicher herunter geladen werden. Der Upload funktioniert auch nicht immer, aber meistens schon. Doch beim Herunterladen erhalte ich den Fehler "System.outOfMemory". Und zwar nach wenigen Sekunden. Das Herunterladen wird über die Funktion gewährleistet.

    VB.NET-Quellcode

    1. Public Function ExecuteSQLQuery(ByRef SQLCommand As SqlCommand, Optional ShowError As Boolean = False, Optional WithSchema As Boolean = False) As DataSet
    2. 'Gewährleistet eine neue Verbindung
    3. If Not IsConnected() Then
    4. Connect()
    5. End If
    6. Try
    7. DS = New DataSet
    8. 'Für XML Support
    9. DS.SchemaSerializationMode = SchemaSerializationMode.IncludeSchema
    10. 'DataAdapter init
    11. SqlAD = New SqlDataAdapter()
    12. 'Versuch die Daten 1:1 mit Primärschlüsseln im Adapter abzubilden.
    13. If WithSchema Then
    14. SqlAD.MissingSchemaAction = MissingSchemaAction.AddWithKey
    15. End If
    16. 'SQL Command und Connection festlegen
    17. SQLCommand.Connection = Conn
    18. SqlAD.SelectCommand = SQLCommand
    19. 'Transaktion gestartet?
    20. If TransStarted = True Then
    21. SqlAD.SelectCommand.Transaction = Trans
    22. End If
    23. 'Abfrage durchführen und die Daten in die temporäre Tabelle Antwort schreiben
    24. SqlAD.Fill(DS, "Antwort")
    25. DS.Tables("Antwort").TableName = "Antwort"
    26. If TransStarted = False Then
    27. Conn.Close()
    28. End If
    29. Catch ex As Exception
    30. If ShowError = True Or DEBUG_MODE = True Then
    31. MessageBox.Show(ex.Message & vbCrLf & SQLCommand.CommandText, "ExecuteSQLQuery", MessageBoxButtons.OK, MessageBoxIcon.Error)
    32. Else
    33. Throw
    34. End If
    35. End Try
    36. 'Rückgabe der Antwort
    37. Return DS
    38. End Function


    Ich erhalte zudem den Hinweis, dass ich die Arraygröße vorher festlegen soll. Aber wie soll ich das hier machen? Der Fehler tritt dann in "catch" auf.
    der Fehler tritt nicht in Catch auf, sondern in Catch wird er gefangen.

    Wenn du wissen willst, wo der Fehler auftritt, mach den TryCatch weg.
    Dann wird der Fehler nicht gefangen, bzw. wird vom Debugger gefangen, und der Debugger unterstützt dich dann um Welten besser als diese dumme Messagebox, die du da derzeit kriegst.
    Weil der Debugger kann nur dann seinen Job machen und Fehler debuggen, wenn sie nicht mit TryCatch weg-gecatcht wurden (Nomen est Omen!)
    Die MessageBox wird gar nicht benutzt. Das hätte ich vielleicht hinzuschreiben sollen. Ich dachte, wenn ich schreibe wo der Fehler auftritt, dann kennt das vielleicht schon jemand.

    Aufgerufen wird die Methode mit

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Gibt den Anhang als DataSet zurück. Inclusive der Binärdaten.
    3. ''' </summary>
    4. ''' <param name="Name"></param>
    5. ''' <returns></returns>
    6. ''' <remarks></remarks>
    7. Public Function GetAnhangDataSet(Name As String) As DataSet
    8. Dim SQLcmd As New SqlCommand("SELECT * FROM Anhang WHERE Name LIKE @Name")
    9. SQLcmd.Parameters.Add("@Name", SqlDbType.NVarChar, 4000)
    10. SQLcmd.Parameters("@Name").Value = Name
    11. SQLcmd.CommandTimeout = 300
    12. Return SQLServer.ExecuteSQLQuery(SQLcmd)
    13. End Function


    Der Fehler tritt in der Zeile

    VB.NET-Quellcode

    1. SqlAD.Fill(DS, "Antwort")


    auf. Und enthält diesen Text:

    Spoiler anzeigen

    Quellcode

    1. System.OutOfMemoryException wurde nicht behandelt.
    2. HResult=-2147024882
    3. Message=Eine Ausnahme vom Typ "System.OutOfMemoryException" wurde ausgelöst.
    4. Source=System.Data
    5. StackTrace:
    6. bei System.Data.SqlTypes.SqlBinary.get_Value()
    7. bei System.Data.SqlClient.SqlBuffer.get_ByteArray()
    8. bei System.Data.SqlClient.SqlBuffer.get_Value()
    9. bei System.Data.SqlClient.SqlDataReader.GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData)
    10. bei System.Data.SqlClient.SqlDataReader.GetValues(Object[] values)
    11. bei System.Data.ProviderBase.DataReaderContainer.CommonLanguageSubsetDataReader.GetValues(Object[] values)
    12. bei System.Data.ProviderBase.SchemaMapping.LoadDataRow()
    13. bei System.Data.Common.DataAdapter.FillLoadDataRow(SchemaMapping mapping)
    14. bei System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn parentChapterColumn, Object parentChapterValue)
    15. bei System.Data.Common.DataAdapter.Fill(DataSet dataSet, String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
    16. bei System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
    17. bei System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
    18. bei System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, String srcTable)
    19. bei Database.SQLServerDB.ExecuteSQLQuery(SqlCommand& SQLCommand, Boolean ShowError, Boolean WithSchema) in C:\Users\XXXX\Documents\Visual Studio 2015\Projects\XXXX\Database\DBA\SQLServerDB.vb: Zeile206
    20. bei Database.SQLServerAbfragen.GetAnhangDataSet(String Name) in C:\Users\XXXX\Documents\Visual Studio 2015\Projects\XXXX\Database\DBA\SQLServerAbfragen.vb: Zeile3081
    21. bei Werkbuchv3._1.ParaDetailWindow.LV_ProzAnhänge_DoubleClick(Object sender, EventArgs e) in C:\Users\XXXX\Documents\Visual Studio 2015\Projects\XXXX\XXXX\Windows\Prozesse\ParaDetailWindow.vb: Zeile170
    22. bei System.Windows.Forms.Control.OnDoubleClick(EventArgs e)
    23. bei System.Windows.Forms.ListView.WndProc(Message& m)
    24. bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    25. bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    26. bei System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    27. bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    28. bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    29. bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    30. bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    31. bei System.Windows.Forms.Application.RunDialog(Form form)
    32. bei System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
    33. bei System.Windows.Forms.Form.ShowDialog()
    34. bei Werkbuchv3._1.ProzessTable.LVProgramm_DoubleClick(Object sender, EventArgs e) in C:\Users\XXXX\Documents\Visual Studio 2015\Projects\XXXX\XXXX\Windows\Prozesse\ProzessTable.vb: Zeile367
    35. bei System.Windows.Forms.Control.OnDoubleClick(EventArgs e)
    36. bei System.Windows.Forms.ListView.WndProc(Message& m)
    37. bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    38. bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    39. bei System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    40. bei System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    41. bei System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    42. bei System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    43. bei System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    44. bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
    45. bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
    46. bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
    47. bei Werkbuchv3._1.My.MyApplication.Main(String[] Args) in : Zeile81
    48. InnerException:


    Der Fehler wird in der Anwendung mit

    VB.NET-Quellcode

    1. Throw
    durchgereicht und Zentral in der Anwendung abgefangen. Das gewährleistet auch das der Debugger durchaus seinen Dienst verrichtet. Aber ich kann damit nichts anfangen.

    Quellcode

    1. Stellen Sie sicher, dass genügend Arbeitsspeicher für interne Zwecke oder neue verwaltete Objekte zur Verfügung steht.
    2. Stellen Sie bei Erstellen eines Arrays sicher, dass die Größe richtig ist.


    Der Punkt ist, dass ich darauf aber scheinbar keinen Einfluss habe, da der Fehler im SqlAdapter selbst aufzutreten scheint. Außer ich habe etwas übersehen? Am Arbeitsspeicher selber kann es nicht liegen. Es stehen 12 GB zur Verfügung. Wie gesagt: Normalerweise funktioniert das auch. Nur bei einer 700MB Datei hängt sich die Anwendung mit dieser Meldung auf. Das Feld in der DB ist (Image). Muss ich jetzt den Stack prüfen bevor ich diese Abfrage abfeuere? Und woher weiß ich was die maximale Größe dabei sein kann? Wie gesagt ich kann zur Zeit nichts damit anfangen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Sio_x“ ()

    tja, keine Ahnung

    Probier halt, ob die einfache Einfach-Version wenigstens geht:

    VB.NET-Quellcode

    1. Public Function ExecuteSQLQuery(ByRef SQLCommand As SqlCommand, Optional ShowError As Boolean = False, Optional WithSchema As Boolean = False) As DataSet
    2. Dim DS = New DataSet
    3. SQLCommand.Connection = conn
    4. Using SqlAD = New SqlDataAdapter(SQLCommand)
    5. SqlAD.Fill(ds)
    6. End Using
    7. Return DS
    8. End Function
    Aber gut möglich, dass man eben keine 500MB-Dateien in ein Dataset laden kann.
    Lass dir auch mal den Namen des Films ausgeben - vlt. ist das ja auch ein Name-Fragment, sodass der Like-Operator gleich mehrere Filme findet.
    weil dann wärs wohl ganz logisch, wenn das iwann in eine OutOfMemory endet.

    ErfinderDesRades schrieb:

    tja, keine Ahnung

    Probier halt, ob die einfache Einfach-Version wenigstens geht:

    VB.NET-Quellcode

    1. Public Function ExecuteSQLQuery(ByRef SQLCommand As SqlCommand, Optional ShowError As Boolean = False, Optional WithSchema As Boolean = False) As DataSet
    2. Dim DS = New DataSet
    3. SQLCommand.Connection = conn
    4. Using SqlAD = New SqlDataAdapter(SQLCommand)
    5. SqlAD.Fill(ds)
    6. End Using
    7. Return DS
    8. End Function
    Aber gut möglich, dass man eben keine 500MB-Dateien in ein Dataset laden kann.
    Lass dir auch mal den Namen des Films ausgeben - vlt. ist das ja auch ein Name-Fragment, sodass der Like-Operator gleich mehrere Filme findet.
    weil dann wärs wohl ganz logisch, wenn das iwann in eine OutOfMemory endet.

    Es werden keine Filme gehostet. Es ist eine Anwendung in der teilweise mehr als 100 Bilder eines X-Rays in einer ZIP Datei verwaltet werden. Normalerweise werden die Bilder einzeln in der DB gespeichert und das ist kein Problem. Aber manchmal lädt auch jemand die Bilder in einer ZIP hoch. Der User, der die Daten sehen möchte, bekommt dann eine Fehlermeldung und meldet sich bei mir. Damit keine doppelten Dateien gefunden werden, erweitere ich den Dateinamen um einen Timestamp. So wird aus C2100.jpg (635708443985759830C2100.jpg). Das verwende ich dann auch gleich um das Uploaddatum anzuzeigen. Gleichzeitig erschlage ich damit die typischen Dateinamen die durch Digitalkameras erzeugt werden.

    Für große Dateien hatte ich aber extra ein BLOB (Binary Lage Object) Feld (Datentyp image) in der DB bereit gestellt. Damit können Dateien bis 2 GB gespeichert werden.

    OMA schrieb:

    Wieviel Arbeitsspeicher hat die Maschine mit der du arbeitest?


    Sio_x schrieb:

    Es stehen 12 GB zur Verfügung.

    Davon sind vielleicht 5 GB belegt.

    ErfinderDesRades schrieb:

    jo, die Frage ist, ob man ein 500MB-Blob in ein Dataset laden kann.

    Genau.
    Und vor allem - warum sollte man so etwas tun?
    Wie wäre es mit einem FileStream? Da gibts dann z.B. ein schönes ByteArray zur Weiterverarbeitung.
    Dies hier ist ganz interessant: LINK

    Auszug:

    C#-Quellcode

    1. using (SqlConnection connection = new SqlConnection("ConnectionString"))
    2. {
    3. connection.Open();
    4. using (SqlCommand command =
    5. new SqlCommand("select BlobData from TestTable", connection))
    6. {
    7. byte[] buffer = (byte[])command.ExecuteScalar();
    8. using (FileStream fs = new FileStream
    9. (@"DeineDateiHierZusammenknoten.xxx", FileMode.Create))
    10. {
    11. fs.Write(buffer, 0, buffer.Length);
    12. }
    13. }
    14. }