2010-05-19 17 views
7

Meine Methode, die SQL Server aufruft gibt eine DataReader zurück, aber wegen dem, was ich tun muss - die DataReader an die aufrufende Methode zurückgibt, die in einem Seitencode-behind liegt - kann ich die Verbindung in der Klasse der nicht schließen Methode, die SQL Server aufruft. Aus diesem Grund habe ich keine oder blockweise.Soll ich IDisposable hier implementieren?

Ist die richtige Art der Ressourcen zu entsorgen, um die Klasse zu implementieren IDisposable? Alternativ sollte ich die nicht verwaltete Ressource (Felder auf Klassenebene) vom Aufrufer explizit disponieren?

EDIT: Ich sende das Datareader zurück, weil ich bestimmte Daten aus dem Datareader zu einer listitem Kontrolle binden muß, so in der anrufende Klasse (Codebehind-Seite), das tue ich:

new ListItem(datareader["dc"]); (along those lines). 
+9

Warum wollen Sie das Datenlesegerät auf der Seite senden? – Perpetualcoder

+0

Die direkte Rückgabe des DataReader kann eine schlechte Übung sein, aber in manchen Fällen kann es für ihn nützlich sein. – Venemo

+0

@Venemo - Ich denke, dass http://stackoverflow.com/questions/2867661/should-i-implement-idisposable-here/2869503#2869503 ihm besser dienen könnte. – dss539

Antwort

7

Ich würde sagen ja , implementieren IDisposable. Einer der Hauptgründe, soweit ich es sagen kann, ist, wenn man dem Benutzer des Objekts nicht genug vertrauen kann, um es selbst richtig zu machen. Dies scheint ein Hauptkandidat dafür zu sein.

Dies gesagt jedoch, es ist eine Frage zu Ihrer Architektur. Warum möchten Sie das DataReader selbst an die Seite senden, anstatt eine Methode aufzurufen, die es für Sie erledigt (einschließlich der relevanten Bereinigung), indem Sie das Notwendige zurücksenden? Wenn es notwendig ist, dem Leser den eigentlichen Leser zu geben, dann sei es so.

3

Ja, Sie sollten IDisposable in Ihrer benutzerdefinierten Klasse implementieren, wenn sie einen DataReader enthält, der geöffnet ist, wenn er auf eine niedrigere Ebene zurückgegeben wird.

Es ist das akzeptierte Muster bei der Rückgabe von etwas, das aufgeräumt werden muss.

2

Ihre Klasse

class MyClass : IDisposable 
{ 
    protected List<DataReader> _readers = new List<DataReader>(); 
    public DataReader MyFunc() 
    { 
     ///... code to do stuff 

     _readers.Add(myReader); 
     return myReader; 
    } 
    private void Dispose() 
    { 
     for (int i = _readers.Count - 1; i >= 0; i--) 
     { 
      DataReader dr = _reader.Remove(i); 
      dr.Dispose(); 
     } 
     _readers = null; 

     // Dispose/Close Connection 
    } 
} 

außerhalb Ihrer Klasse Dann

public void FunctionThatUsesMyClass() 
{ 
    using(MyClass c = new MyClass()) 
    { 
     DataReader dr = c.MyFunc(); 
    } 
} 

Alle Leser und die MyClass Instanz aufgeräumt werden, wenn die using Block beendet.

+0

Warum sollten Sie sie aus der '_readers'-Variable entfernen? Und das Setzen von '= null' wird eigentlich nichts bewirken. –

+0

Sie aus _readers zu entfernen, ist eine gute Möglichkeit, sie zu de-referenzieren, indem Sie dem GC im Grunde sagen, dass er sie bereinigen kann. – Venemo

+0

'_readers = null' war nur aus Gewohnheit. Sie brauchen es nicht, denn wenn das 'MyClass'-Objekt GC'ed ist, wird auch die leere Liste. Wenn Sie sie aus dem _readers -Objekt entfernen, wird die Anzahl der Referenzen verringert und die Garbage Collection unterstützt. Der GC erkennt schließlich, dass nur Referenzen auf ausstehende Objekte für die Sammlung vorhanden sind, aber es kann nicht schaden. – Aren

3

Erstens, die DataReader übergeben möglicherweise nicht wirklich, was Sie tun möchten, aber ich nehme an, dass es ist.

Die richtige Methode, um dies zu behandeln, würde sein, einen zusammengesetzten Typ zurückzugeben, der die DataReader einkapselt oder exponiert und an der Verbindung festhält, dann implementieren Sie IDisposable für diesen Typ. Wenn Sie diesen Typ entsorgen, entsorgen Sie sowohl den Reader als auch die Verbindung.

4

Wenn Sie die Datenbankverbindung als Membervariable in Ihrer Leserklasse halten und Ihre Leseklasse IDisposable implementieren lassen, scheint das für mich in Ordnung zu sein.

Sie könnten jedoch in Betracht ziehen, dass Ihre Methode IEnumerable zurückgibt und yield return Anweisungen verwendet, um durch den Datenleser zu gehen. Auf diese Weise können Sie die Ergebnisse zurückgeben und dennoch innerhalb Ihrer Methode bereinigen.

Hier ist eine grobe Skizze von dem, was ich meine:

public IEnumerable<Person> ReadPeople(string name) 
{ 
    using (var reader = OpenReader(...)) 
    { 
     // loop through the reader and create Person objects 
     for ... 
     { 
      var person = new Person(); 
      ... 
      yield return person; 
     } 
    } 
} 
+0

...oder alternativ "public IEnumerable " mit "yield return reader;" und überlasse es dem Anrufer, wie er die Daten verarbeitet. – Joe

+0

Ich habe in der Vergangenheit sehr ähnlich gemacht. Ich generierte es zu einer Klasse, die nur die Verbindung und den Leser verkapselte - aber Sie konnten sie kaskadieren (so können Sie immer noch mehrere Leser haben). Arbeitete sehr gut – philsquared

1

Die allgemeine Regel ist, dass Ihre Klasse IDisposable implementieren sollten, wenn sie direkt nicht verwalteten Ressourcen hält oder enthält einen Verweis auf ein anderes Objekt IDisposable. Wenn Ihre Klasse IDataReader in einer Methode erstellt, diese Referenz aber nie enthält, müsste Ihre Klasse IDisposable nicht implementieren (es sei denn, sie enthält einfach eine IDisposable neben der IDataReader, die in dieser Methode erstellt wurde).

Die eigentliche Frage, die Sie sich stellen müssen, ist, ob Ihre Klasse wirklich auf dem IDataReader halten sollte, selbst nachdem es es an den Anrufer geliefert hat. Persönlich halte ich das für ein schlechtes Design, weil es die Eigentumslinie verwischt. Wer besitzt die IDisposable in diesem Fall? Wer ist für seine Lebenszeit verantwortlich? Nehmen Sie zum Beispiel die IDbCommand Klassen. Sie erstellen IDataReader Instanzen und geben sie an die Aufrufer zurück, aber geben sich selbst frei. Das macht die API sauber und die Verantwortung für die Lebensdauerverwaltung ist in diesem Fall eindeutig.

Unabhängig von der Eigentumsfrage erfordert Ihre spezifische Situation die Implementierung von IDisposable; Nicht, weil Ihre Klasse zufällig eine IDataReader Instanz erstellt und zurückgibt, sondern weil es sich anhört, als ob sie ein IDbConnection Objekt enthält.

2

Ich würde nichts zurückgeben. Stattdessen würde ich einen Delegierten übergeben.

Zum Beispiel:

void FetchMeSomeReader(Action<IDataReader> useReader) 
{ 
    using(var reader = WhateverYouDoToMakeTheReader()) 
     useReader(reader); 
} 

Dann in Ihrer Berufung Klasse:

void Whatever() 
{ 
    FetchMeSomeReader(SetFields); 
} 

void SetFields(IDataReader reader) 
{ 
    MyListItem = new ListItem(datareader["dc"]); 
} 
Verwandte Themen