2010-04-03 19 views
5

Tengo dos preguntas.Usando la pregunta de estado

1) ¿Debería usar siempre una instrucción using en una conexión? Entonces, ¿lo usaría en la conexión y luego otro en un lector dentro de la conexión? Entonces usaría dos declaraciones de uso.

2) Supongamos que utiliza la instrucción using en la conexión y también un lector que se devuelve en la conexión. Entonces tienes dos declaraciones de uso. ¿Crea dos bloques Try {} Finally {} o solo uno?

Gracias!

Respuesta

4
  1. siempre debe utilizar una declaración using cuando un objeto implementa IDisposable. Esto incluye conexiones.

  2. Creará dos bloques anidados try{}finally{}.

+0

No debe _siempre_ usar una instrucción 'using' - un caso donde pueden surgir problemas es cuando se llama' Command.ExecuteReader'. Este método puede arrojar excepciones, y si lo hace, no se manejarán correctamente y su software fallará. Es mejor manejar las posibles excepciones y llamar manualmente a 'Dispose()' o su equivalente. – Conrad

1

responder a cada uno:

1) Sí, esto sería la mejor práctica para disponer tanto, tan pronto como sea posible.

2) using() creará dos bloques, envueltos entre sí en el mismo orden. Deshairá el objeto interno (el lector) primero, luego dispondrá el objeto del exterior usando (la conexión).

7

Ten cuidado aquí. Debe siempre tener una instrucción using en cualquier objeto local que implemente IDisposable. Eso incluye no solo conexiones y lectores, sino también el comando. Pero puede ser difícil a veces exactamente donde va que usa la instrucción. Si no tiene cuidado, puede causar problemas. Por ejemplo, en el código que sigue a la instrucción using cerrará su lector antes de que usted llegue a usarlo:

DataReader MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      return rdr; 
     } 
    } 
} 

En su lugar, tienen cuatro opciones. Una de ellas es que esperar para crear el bloque usando hasta que llame a la función:

DataReader MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     return cmd.ExecuteReader(); 
    } 
} 

using (var rdr = MyQuery()) 
{ 
    while (rdr.Read()) 
    { 
     //... 
    } 
} 

Por supuesto, usted todavía tiene que cuidado con su conexión allí y significa recordar a escribir un bloque usando todas partes se utiliza la función.

La opción dos solo procesa los resultados de la consulta en el método en sí, pero eso rompe la separación de su capa de datos del resto del programa. Una tercera opción es que su función MyQuery() acepte un argumento de tipo Acción que puede llamar dentro del ciclo while (rdr.Read()), pero eso es incómodo.

por lo general prefieren la opción de cuatro: gire el lector de datos en un IEnumerable, así:

IEnumerable<IDataRecord> MyQuery() 
{ 
    string sql="some query"; 
    using (var cn = new SqlConnection("connection string")) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return rdr; 
     } 
    } 
} 

Ahora todo se cerrará correctamente y el código que se encarga de todo está en un solo lugar. También obtienes una buena bonificación: los resultados de tu consulta funcionarán bien con cualquiera de los operadores de linq.

Por último, algo nuevo que estoy jugando con la próxima vez que llego a construir un proyecto completamente nuevo que combina el IEnumerable con el paso en un argumento delegado:

//part of the data layer 
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters) 
{ 
    //DL.ConnectionString is a private static property in the data layer 
    // depending on the project needs, it can be implementing to read from a config file or elsewhere 
    using (var cn = new SqlConnection(DL.ConnectionString)) 
    using (var cmd = new SqlCommand(sql, cn)) 
    { 
     addParameters(cmd.Parameters); 

     cn.Open(); 
     using (var rdr = cmd.ExecuteReader()) 
     { 
      while (rdr.Read()) 
       yield return rdr; 
     } 
    } 
} 

Y luego lo usaré dentro de la capa de datos de esta manera:

public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID) 
{ 
    //I could easily use a stored procedure name instead, and provide overloads for commandtypes. 
    return Retrieve(
     "SELECT c.* 
     FROM [ParentTable] p 
     INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
     WHERE f.ID= @ParentID", p => 
     { 
      p.Add("@ParentID", SqlDbType.Int).Value = ParentID; 
     } 
    ); 
} 
+2

"Siempre debe tener una sentencia using en cualquier objeto que implemente IDisposable": bueno, eso no es exactamente cierto ... Debe hacer eso para variables ** locales ** que implementen IDisposable, pero no para miembros de clase, ya que lo harán por lo general, se reutiliza en otro lugar de la clase. Sin embargo, en ese caso la clase debe implementar IDisposable, y disponer todos los miembros IDisposable en su método Dispose –

+0

Tenga en cuenta también que las clases que realizan un "rendimiento de retorno" dentro de un bloque using/try/finally implementarán IDisposable, y no se eliminarán correctamente. ellos darán como resultado que el código de uso-Disposición o finalmente no se ejecute. – supercat

5

1) en caso de que utilice siempre un comunicado mediante una conexión? Entonces, ¿lo usaría en la conexión y luego otro en un lector dentro de la conexión ? Entonces usaría dos usando declaraciones.

Sí, porque implementan IDisposable. Y no se olvide de una declaración using en el comando también:

using (DbConnection connection = GetConnection()) 
using (DbCommand command = connection.CreateCommand()) 
{ 
    command.CommandText = "SELECT FOO, BAR FROM BAZ"; 
    connection.Open(); 
    using (DbDataReader reader = command.ExecuteReader()) 
    { 
     while (reader.Read()) 
     { 
      .... 
     } 
    } 
} 

2) Digamos que utiliza el uso de declaración sobre la conexión y también un lector de de ser devuelto en la conexión . Entonces tiene dos usando declaraciones . ¿Crea dos bloques Try {} Finally {} o solo uno?

Cada declaración using va a crear su propio bloque de try/finally

2

Punto especial el 1). Necesitará específicamente evitar esa técnica cuando la conexión se usa en asynchronous ADO.NET methods - como BeginExecuteReader, porque lo más probable es que se salga del alcance e intente eliminar la conexión mientras la operación asíncrona aún está en progreso. Esto es similar al caso cuando usa variables de clase y no variables locales. Muchas veces la referencia de conexión se almacena en una clase utilizada como el "bloque de control" para la operación asincrónica.

Cuestiones relacionadas