2016-07-08 14 views
1

Ich weiß, dass ich DataAdapter-Instanzen immer entsorgen sollte. In den meisten Fällen entsorge ich es sofort nach dem Schließen der Verbindung, aber in Fällen wie wenn Benutzer DataTable Elemente ändern (angezeigt in ListBox oder DataGridView) ich den DataAdapter erstellen, verwenden Sie es, um die DataTable zu füllen, aber nicht disponieren bis der Benutzer klickt Save die DataAdapter.Update(DataTable) ... ruft nicht meine Hauptfrage, aber ist das der richtige Ansatz?DataAdapter wird vor Erreichen von "End Using" entfernt

Zurück zur Hauptfrage, ich habe diese beiden Funktionen:

Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean 
    Using daCompanies As MySqlDataAdapter = Nothing 
     Return LoadCompaniesDT(daCompanies, dtCompanies) 
    End Using 
End Function 

Public Function LoadCompaniesDT(ByRef daCompanies As MySqlDataAdapter, ByRef dtCompanies As DataTable) As Boolean 
    Dim sql As String = "SELECT * FROM companies" 
    Return LoadDT(daCompanies, dtCompanies, sql, Res.CompaniesFailedMsgBody) 
End Function 

Sie werden verwendet, LoadDT zu nennen, die die Datatable füllt so dass ich die Wahl habe einen Dataadapter oder nicht passieren.

Jetzt bin ich etwas verwirrt: Wenn die erste LoadCompaniesDT Funktion wird daCompanies angeordnet, bevor End Using erreicht .. wie folgt aus:

Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean 
    Using daCompanies As MySqlDataAdapter = Nothing 
     Dim tmp As Boolean = LoadCompaniesDT(daCompanies, dtCompanies) 
     Console.WriteLine(daCompanies Is Nothing) ' ==> True!! 
     Return tmp 
    End Using 
End Function 

Hinweis: Wenn ich Dim daCompanies statt Using daCompanies verwenden dann daCompanies Is Nothing zurückkehren Falsch.


LoadDT Funktionscode:

Private Function LoadDT(ByRef da As MySqlDataAdapter, ByRef dt As DataTable, 
                 ByVal sqlQuery As String, 
                 ByVal errorText As String) As Boolean 
    Dim connStr As String = String.Format("server={0}; port={1}; user id={2}; password={3}; database={4}", 
              DbServer, DbServerPort, DbUserName, DbPassword, DatabaseName) 
    Dim conn As MySqlConnection = New MySqlConnection(connStr) 
    Dim cmd As MySqlCommand = New MySqlCommand 

    Try 
     conn.Open() 

     cmd.CommandType = CommandType.Text 
     cmd.CommandText = sqlQuery 
     cmd.Connection = conn 

     da = New MySqlDataAdapter(cmd) 
     dt = New DataTable 
     da.Fill(dt) 

     Return True 
    Catch ex As Exception 
     MessageBox.Show(errorText, Res.ServerError, MessageBoxButtons.OK, MessageBoxIcon.Error) 
     Return False 
    Finally 
     cmd.Dispose() 
     cmd = Nothing 
     conn.Close() 
     conn.Dispose() 
    End Try 
End Function 
+0

Sie sollten den Titel ändern, um "ByRef" einzuschließen, weil das der Kern dieses Problems ist –

Antwort

1

aktualisieren: Sie haben Recht, man bekommt von den Methoden keine initialisiert MySqlDataAdapter zurück, wenn die ByRef bestanden Instanz in einem Using verwendet wird - Erklärung. Diese Variablen sind schreibgeschützt. In C# erhalten Sie this sinnvoll Compiler-Fehler:

Error CS1657 Cannot pass 'daCompanies' as a ref or out argument because it is a 'using variable'

Es here dokumentiert ist:

Compiler Error CS1657

Cannot pass 'parameter' as a ref or out argument because 'reason'' This error occurs when a variable is passed as a ref or out argument in a context in which that variable is readonly. Readonly contexts include foreach iteration variables, using variables, and fixed variables.

in VB.NET Sie das tun können (so dass der Compiler ignoriert es die fast ein Fehler ist), aber die Variable wird danach nicht initialisiert. Aber wie unten erwähnt, sollten Sie diesen Ansatz nicht verwenden.


Nach der anderen Frage:

Wenn Sie auf die Probe schauen auf MSDN sehen Sie, dass Microsoft auch nicht die Dataadapter verfügen. Es ist also nicht wirklich notwendig. Nachdem dies gesagt wurde, ist es immer die beste Vorgehensweise, die Using-Anweisung für alles zu verwenden, das IDisposable implementiert.

Ein DataAdapter ist kein teures Objekt und enthält keine nicht verwalteten Ressourcen (wie die Verbindung). Es tut also nicht weh, eine neue Instanz daraus zu erstellen, wo immer man sie braucht. Und Sie müssen es nicht entsorgen, aber das ist ein Implementierungsdetail, das sich in Zukunft oder in einer anderen Implementierung von DbDataAdapter ändern könnte, so dass es immer noch die beste Vorgehensweise ist, es zu entsorgen, am besten mit der Using -Statement.

Ich würde Ihren Ansatz nicht verwenden, weil Sie die SQL-Zeichenfolge an die Methode übergeben, die oft zu einer SQL-Injection-Sicherheitslücke führt. Verwenden Sie stattdessen SQL-Parameter.

Zum Beispiel:

Private Function LoadDT() As DataTable 
    Dim tbl As New DataTable() 
    'Load connection string from app.config or web.config 
    Dim sql As String = "SELECT * FROM companies" ' don't use * but list all columns explicitely 
    Using conn As New MySqlConnection(My.Settings.MySqlConnection) 
     Using da = New MySqlDataAdapter(sql, conn) 
      da.Fill(tbl) 
     End Using 
    End Using 
    Return tbl 
End Function 

Statt eine errorTextByRef zugeben ich einen Logging-Framework wie log4net verwenden würde.

+0

Ja, das ist der Eindruck, den ich von [dieser] (http://stackoverflow.com/q/18205560/4934172) Frage bekommen habe. Warum also wird es entsorgt, bevor das Ende der 'Using'-Anweisung erreicht wird? –

+0

@GeniuSBraiN: Ich habe meine Antwort bearbeitet –

+0

Ich sehe. Danke für Ihre Mühe, das hat geholfen :) –