2012-04-18 8 views
6

Estoy tratando de obtener información de columna en C# desde una tabla SQL en SQL Server. Estoy siguiendo el ejemplo en este enlace: http://support.microsoft.com/kb/310107 Mi programa extrañamente se cuelga cuando intenta cerrar la conexión. Si la conexión no está cerrada, el programa se cierra sin ninguna excepción. Aquí está mi código:¿Por qué mi aplicación se bloquea al intentar cerrar un objeto SqlConnection?

SqlConnection connection = new SqlConnection(@"MyConnectionString"); 
connection.Open(); 
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection); 
SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast. 
DataTable table = reader.GetSchemaTable(); 
Console.WriteLine(table.Rows.Count); 
connection.Close(); // Alternatively If this line is commented out, the program runs fast. 

Poner el SqlConnection dentro de un bloque usando también hace que la aplicación se bloquee a menos CommandBehavior.KeyInfo se cambia a CommandBehavior.SchemaOnly.

using (SqlConnection connection = new SqlConnection(@"MyConnectionString")) 
{ 
    connection.Open(); 
    SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection); 
    SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast even here in the using 
    DataTable table = reader.GetSchemaTable(); 
    Console.WriteLine(table.Rows.Count); 
} 

La tabla en cuestión tiene más de 3 millones de filas, pero como yo no soy más que la obtención de la información de esquema, yo creo que esto no sería un problema. Mi pregunta es: ¿Por qué mi aplicación se atasca al intentar cerrar una conexión?

SOLUCIÓN: Quizás esto no sea óptimo, pero funciona; He insertado un comunicado command.Cancel(); justo antes Close se llama al conectar: ​​

SqlConnection connection = new SqlConnection(@"MyConnectionString"); 
connection.Open(); 
SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection); 
SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); // If this is changed to CommandBehavior.SchemaOnly, the program runs fast. 
DataTable table = reader.GetSchemaTable(); 
Console.WriteLine(table.Rows.Count); 
command.Cancel(); // <-- This is it. 
connection.Close(); // Alternatively If this line is commented out, the program runs fast. 
+0

Puede compartir la cadena de conexión (excluyendo detalles delicados como nombre de usuario, contraseña, servidor y base de datos) para ver si está configurando cualquier otra variable que pueda alargar el proceso de cierre. –

+0

Claro. Aquí está: @ " Fuente de datos = ****; Catálogo inicial = ****; ID de usuario = ****; Contraseña = ****; Tiempo de espera de conexión = 60;" –

+0

¿No debería cerrar el lector primero? – Deb

Respuesta

6

Vi algo así, hace mucho tiempo. Para mí, fue porque hice algo como:

SqlCommand command = new SqlCommand("SELECT * FROM MyTable", connection); 
SqlDataReader reader = command.ExecuteReader(); 

// here, I started looping, reading one record at a time 
// and after reading, say, 100 records, I'd break out of the loop 

connection.Close(); // this would hang 

El problema es que el comando parece querer terminar. Es decir, revisa el conjunto de resultados completo. Y mi conjunto de resultados tenía millones de registros. Terminaría ... eventualmente.

He resuelto el problema agregando una llamada al command.Cancel() antes de llamar al connection.Close().

Consulte http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=610 para obtener más información.

+0

Esto suena prometedor. Lo intentaré cuando llegue al trabajo; ¡Gracias! –

0

Podría probar esto?

DataTable dt = new DataTable(); 
using(SqlConnection conn = new SqlConnection("yourConnectionString")) 
{ 
    SqlCommand cmd = new SqlCommand("SET FMTONLY ON; " + yourQueryString + "; SET FMTONLY OFF;",conn); 
    conn.Open(); 
    dt.Load(cmd.ExecuteReader()); 
} 

SET FMTONLY ON/OFF de MSDN parece el camino a seguir

+0

Me gustaría ir con 'SET FMTONLY ON' excepto que MSDN dice' No use esta característica' junto con SQL Server 2012 (http://msdn.microsoft.com/en-us/library/ms173839%28v=sql .110% 29.aspx). Si bien mi aplicación se relacionará con SQL Server 2008 por el momento, me gustaría que fuera a prueba de futuro, ya que probablemente migremos en el futuro cercano. –

+0

De lo que puedo leer aquí [Función obsoleta en SqlServer 2012] (http://technet.microsoft.com/en-us/library/ms143729 (v = sql.110) .aspx) SET FMTONLY todavía se admite Y __ será eliminado en una versión futura de SQLServer__ Eso significa SQLServer 2014? probablemente todavía tendrá esta declaración. – Steve

+0

CommandBehavior.SchemaOnly prefije una consulta con SET FMTONLY ON. No sé si este valor depende de la versión de SQL o no. Es decir. si FMTONLY está en desuso en una versión futura, ¿lo detectaría .NET a través de la cadena de conexión/conexión y realizaría una operación diferente apropiada para la versión? – ulty4life

0

Hay una forma específica de hacer esto, utilizando SMO (objetos de administración de SQL Server)

Usted puede obtener la colección de tablas en la base de datos, y luego lea las propiedades de la tabla que le interesa (columnas, claves y todas las propiedades imaginables)

Esto es lo que SSMS usa para obtener y establecer propiedades de todas las bases de datos o bjects.

vistazo a esto referencias:

Este es un ejemplo completo de cómo conseguir propiedades de la tabla:

Esto le permitirá obtener toda la información posible de la base de datos de una manera muy fácil. hay muchas muestras en VB.NET y C#.

+0

Supongo que no especifiqué claramente en mi pregunta, pero estoy interesado en encontrar información de columnas de consultas que pueden contener cómputos agregados en la lista de selección. Por lo tanto, la información de la tabla es insuficiente y probablemente sea mucho más problemática que el beneficio limitado que brindará en mi caso. –

0

Intentaré algo como esto. Esto garantiza que todos los elementos se limpien y evita el uso de DataReader. No necesita esto a menos que tenga cantidades inusualmente grandes de datos que podrían causar problemas de memoria.

public void DoWork(string connectionstring) 
    { 
     DataTable dt = new DataTable("MyData"); 
     using (var connection = new SqlConnection(connectionstring)) 
     { 
      connection.Open(); 
      string commandtext = "SELECT * FROM MyTable"; 

      using(var adapter = new SqlDataAdapter(commandtext, connection)) 
      { 
       adapter.Fill(dt); 
      } 
      connection.Close(); 
     } 
     Console.WriteLine(dt.Rows.Count); 
    } 
+0

Básicamente tengo algunas consultas SQL analíticas con agregados y tal. Necesito determinar rápidamente el nombre de la columna y los tipos de los valores devueltos; parece que en su código obtengo el conjunto completo de resultados. Esto es inaceptable en mi caso ya que algunas de mis consultas pueden devolver millones de filas; Solo me interesan los metadatos de la columna. –

+0

Tienes que poner tu propia consulta allí. Intenta usar el esquema de información. seleccione * de INFORMATION_SCHEMA.COLUMNS donde TABLE_NAME como 'mytable' – tsells

Cuestiones relacionadas