2010-05-19 25 views
7

Mi método que llama a SQL Server devuelve un DataReader pero debido a lo que tengo que hacer, que es devolver el DataReader al método de llamada que reside en un código de página detrás, no puedo cerrar la conexión en la clase del método que llama al servidor SQL. Debido a esto, no tengo finalmente ni uso de bloques.¿Debo implementar IDisposable aquí?

¿Es la forma correcta de eliminar los recursos para que la clase implemente IDisposable? Alternativamente, ¿debería disponer explícitamente el recurso no gestionado (campos de nivel de clase) de la persona que llama?

EDIT: yo enviaré la datareader volver porque necesito para enlazar datos específicos de la datareader a un control listitem, por lo que en la clase de llamada (Codebehind página), que hago:

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

¿Por qué desea enviar el lector de datos a la página? – Perpetualcoder

+0

Devolver el DataReader directamente puede ser una mala práctica, pero puede serle útil en algunos casos. – Venemo

+0

@Venemo - Creo que http://stackoverflow.com/questions/2867661/should-i-implement-disposable-here/2869503#2869503 podría servirle mejor. – dss539

Respuesta

7

yo diría que sí , implementar IDisposable. Una de las principales razones por las que puedo decir que lo uso es cuando no puede confiar en el usuario del objeto lo suficiente como para hacerlo correctamente. Este parece ser un candidato principal para eso.

Dicho esto, sin embargo, hay una pregunta para su arquitectura. ¿Por qué desea enviar el DataReader a la página en lugar de llamar a un método para que lo haga por usted (incluida la limpieza correspondiente) devolviendo lo que sea necesario? Si es necesario dar el lector real a la página, entonces que así sea.

3

Sí, debe implementar IDisposable en su clase personalizada si contiene un DataReader que está abierto cuando se lo devuelve a una capa inferior.

Es el patrón aceptado al devolver algo, que debe limpiarse.

2

Su clase

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 
    } 
} 

Luego fuera de su clase

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

Todos los lectores y la MyClass ejemplo, se limpiaron cuando el using salidas de bloque.

+0

¿Por qué molestarse en eliminarlos de la variable '_readers'? Y establecerlo '= null' en realidad no hará nada. –

+0

Eliminarlos de _readers es una gran manera de desreferenciarlos, básicamente diciéndole al GC que puede limpiarlos. – Venemo

+0

'_readers = null' estaba fuera de moda. No lo necesita porque cuando el objeto 'MyClass' está en GC, también lo estará la Lista vacía. Al eliminarlos del objeto _readers se reduce el recuento de referencias y se ayudará en la recolección de elementos no utilizados, eventualmente el GC se dará cuenta de que las únicas referencias a ellos provienen de objetos pendientes para la recolección, pero no puede doler. – Aren

3

En primer lugar, pasar el DataReader puede no ser realmente lo que quiere hacer, pero supongo que lo es.

La forma correcta de manejar esto sería devolver un tipo compuesto que encapsula o expone el DataReader y se aferra a la conexión, luego implementa IDisposable en ese tipo. Al deshacerse de ese tipo, deseche tanto el lector como la conexión.

public class YourClass : IDisposable 
{ 
    private IDbConnection connection; 
    private IDataReader reader; 

    public IDataReader Reader { get { return reader; } } 

    public YourClass(IDbConnection connection, IDataReader reader) 
    { 
     this.connection = connection; 
     this.reader = reader; 
    } 

    public void Dispose() 
    { 
     reader.Dispose(); 
     connection.Dispose(); 
    } 
} 
4

Sosteniendo la conexión de base de datos como una variable miembro de la clase de lector y hacer que su clase lector de implementar IDisposable parece bien a mí.

Sin embargo, puede considerar hacer que su método devuelva IEnumerable y usar las declaraciones yield return para recorrer el lector de datos. De esta forma, puede devolver los resultados y seguir limpiando desde su método.

Aquí hay un bosquejo de lo que quiero decir:

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

...o alternativamente, "public IEnumerable " con "yield return reader"; y dejarle a la persona que llama cómo procesar los datos. – Joe

+0

He hecho algo muy similar en el pasado. Lo generalicé a una clase que simplemente encapsuló la conexión y el lector, pero podrías conectarlos en cascada (para que puedas tener múltiples lectores). Funcionó bastante bien – philsquared

1

La regla general es que la clase debe implementar IDisposable si posee, directa o recursos no administrados que contiene una referencia a otro objeto IDisposable. Si su clase crea un IDataReader en un método pero nunca contiene esa referencia, entonces su clase no necesitaría implementar IDisposable según la regla (a menos que ocurra que contenga un IDisposable además del IDataReader creado en ese único método).

La verdadera pregunta que debe hacerse es si su clase realmente debería retener ese IDataReader incluso después de que se lo haya entregado a la persona que llama. Personalmente, creo que es un diseño pobre porque borra la línea de propiedad. ¿Quién es el propietario del IDisposable en ese caso? ¿Quién es responsable de su tiempo de vida? Tome las clases IDbCommand por ejemplo. Crean instancias IDataReader y las devuelven a las personas que llaman, pero se liberan de la propiedad. Eso hace que la API sea limpia y la responsabilidad de la administración de por vida no es ambigua en ese caso.

Independientemente del problema de la propiedad, su situación específica requiere la implementación de IDisposable; no porque tu clase haya creado y devuelto una instancia IDataReader, sino porque parece que contiene un objeto IDbConnection.

2

No devolvería nada. En cambio, pasaría a un delegado.

Por ejemplo:

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

Luego, en su clase de llamadas:

void Whatever() 
{ 
    FetchMeSomeReader(SetFields); 
} 

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