Rellene la tabla de datos del lector de datos

Estoy haciendo algo básico en C # (MS VS2008) y tengo una pregunta más sobre el diseño adecuado que el código específico.

Estoy creando una tabla de datos y luego bash cargar la tabla de datos de un lector de datos (que se basa en un procedimiento almacenado de SQL). Lo que me pregunto es si la forma más eficiente de cargar la tabla de datos es hacer una statement de tiempo, o si hay una mejor manera.

Para mí, el único inconveniente es que tengo que escribir manualmente en los campos que quiero agregar en mi sentencia while, pero tampoco sé cómo automatizarlo, ya que no quiero que todos los campos de SP solo seleccionen los que quieran. , pero eso no es un gran problema para mí.

He incluido fragmentos de código debajo de la totalidad de lo que hago, aunque para mí el código en sí no es notable o incluso lo que estoy preguntando. Al preguntarme sobre mi metodología, molestaré con la ayuda del código más adelante si mi estrategia es incorrecta / ineficiente.

var dtWriteoffUpload = new DataTable(); dtWriteoffUpload.Columns.Add("Unit"); dtWriteoffUpload.Columns.Add("Year"); dtWriteoffUpload.Columns.Add("Period"); dtWriteoffUpload.Columns.Add("Acct"); dtWriteoffUpload.Columns.Add("Descr"); dtWriteoffUpload.Columns.Add("DEFERRAL_TYPE"); dtWriteoffUpload.Columns.Add("NDC_Indicator"); dtWriteoffUpload.Columns.Add("Mgmt Cd"); dtWriteoffUpload.Columns.Add("Prod"); dtWriteoffUpload.Columns.Add("Node"); dtWriteoffUpload.Columns.Add("Curve_Family"); dtWriteoffUpload.Columns.Add("Sum Amount"); dtWriteoffUpload.Columns.Add("Base Curr"); dtWriteoffUpload.Columns.Add("Ledger"); cmd = util.SqlConn.CreateCommand(); cmd.CommandTimeout = 1000; cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "proc_writeoff_data_details"; cmd.Parameters.Add("@whoAmI", SqlDbType.VarChar).Value = WindowsIdentity.GetCurrent().Name; cmd.Parameters.Add("@parmEndDateKey", SqlDbType.VarChar).Value = myMostRecentActualDate; cmd.Parameters.Add("@countrykeys", SqlDbType.VarChar).Value = myCountryKey; cmd.Parameters.Add("@nodekeys", SqlDbType.VarChar).Value = "1,2"; break; dr = cmd.ExecuteReader(); while (dr.Read()) { dtWriteoffUpload.Rows.Add(dr["country name"].ToString(), dr["country key"].ToString()); } 

Puede cargar una DataTable directamente desde un lector de datos utilizando el método Load() que acepta un IDataReader .

 var dataReader = cmd.ExecuteReader(); var dataTable = new DataTable(); dataTable.Load(dataReader); 

Por favor, compruebe el siguiente código. Automáticamente se convertirá como DataTable

 private void ConvertDataReaderToTableManually() { SqlConnection conn = null; try { string connString = ConfigurationManager.ConnectionStrings["NorthwindConn"].ConnectionString; conn = new SqlConnection(connString); string query = "SELECT * FROM Customers"; SqlCommand cmd = new SqlCommand(query, conn); conn.Open(); SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); DataTable dtSchema = dr.GetSchemaTable(); DataTable dt = new DataTable(); // You can also use an ArrayList instead of List<> List listCols = new List(); if (dtSchema != null) { foreach (DataRow drow in dtSchema.Rows) { string columnName = System.Convert.ToString(drow["ColumnName"]); DataColumn column = new DataColumn(columnName, (Type)(drow["DataType"])); column.Unique = (bool)drow["IsUnique"]; column.AllowDBNull = (bool)drow["AllowDBNull"]; column.AutoIncrement = (bool)drow["IsAutoIncrement"]; listCols.Add(column); dt.Columns.Add(column); } } // Read rows from DataReader and populate the DataTable while (dr.Read()) { DataRow dataRow = dt.NewRow(); for (int i = 0; i < listCols.Count; i++) { dataRow[((DataColumn)listCols[i])] = dr[i]; } dt.Rows.Add(dataRow); } GridView2.DataSource = dt; GridView2.DataBind(); } catch (SqlException ex) { // handle error } catch (Exception ex) { // handle error } finally { conn.Close(); } } 

Si está intentando cargar una DataTable , apalanque el SqlDataAdapter lugar:

 DataTable dt = new DataTable(); using (SqlConnection c = new SqlConnection(cString)) using (SqlDataAdapter sda = new SqlDataAdapter(sql, c)) { sda.SelectCommand.CommandType = CommandType.StoredProcedure; sda.SelectCommand.Parameters.AddWithValue("@parm1", val1); ... sda.Fill(dt); } 

Ni siquiera necesita definir las columnas. Simplemente cree la DataTable y DataTable .

Aquí, cString es su cadena de conexión y sql es el comando de procedimiento almacenado.

Como dijo Sagi en su respuesta, DataTable.Load es una buena solución. Si intenta cargar varias tablas desde un solo lector, no necesita llamar a DataReader.NextResult. El método DataTable.Load también avanza al lector al siguiente conjunto de resultados (si corresponde).

 // Read every result set in the data reader. while (!reader.IsClosed) { DataTable dt = new DataTable(); // DataTable.Load automatically advances the reader to the next result set dt.Load(reader); items.Add(dt); } 

También investigué esto y, después de comparar el método SqlDataAdapter.Fill con los funcitiones SqlDataReader.Load, descubrí que el método SqlDataAdapter.Fill es más del doble de rápido con los conjuntos de resultados que he estado usando

Código usado:

  [TestMethod] public void SQLCommandVsAddaptor() { long AdapterFillLargeTableTime, readerLoadLargeTableTime, AdapterFillMediumTableTime, readerLoadMediumTableTime, AdapterFillSmallTableTime, readerLoadSmallTableTime, AdapterFillTinyTableTime, readerLoadTinyTableTime; string LargeTableToFill = "select top 10000 * from FooBar"; string MediumTableToFill = "select top 1000 * from FooBar"; string SmallTableToFill = "select top 100 * from FooBar"; string TinyTableToFill = "select top 10 * from FooBar"; using (SqlConnection sconn = new SqlConnection("Data Source=.;initial catalog=Foo;persist security info=True; user id=bar;password=foobar;")) { // large data set measurements AdapterFillLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteDataAdapterFillStep); readerLoadLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteSqlReaderLoadStep); // medium data set measurements AdapterFillMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteDataAdapterFillStep); readerLoadMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteSqlReaderLoadStep); // small data set measurements AdapterFillSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteDataAdapterFillStep); readerLoadSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteSqlReaderLoadStep); // tiny data set measurements AdapterFillTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteDataAdapterFillStep); readerLoadTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteSqlReaderLoadStep); } using (StreamWriter writer = new StreamWriter("result_sql_compare.txt")) { writer.WriteLine("10000 rows"); writer.WriteLine("Sql Data Adapter 100 times table fill speed 10000 rows: {0} milliseconds", AdapterFillLargeTableTime); writer.WriteLine("Sql Data Reader 100 times table load speed 10000 rows: {0} milliseconds", readerLoadLargeTableTime); writer.WriteLine("1000 rows"); writer.WriteLine("Sql Data Adapter 100 times table fill speed 1000 rows: {0} milliseconds", AdapterFillMediumTableTime); writer.WriteLine("Sql Data Reader 100 times table load speed 1000 rows: {0} milliseconds", readerLoadMediumTableTime); writer.WriteLine("100 rows"); writer.WriteLine("Sql Data Adapter 100 times table fill speed 100 rows: {0} milliseconds", AdapterFillSmallTableTime); writer.WriteLine("Sql Data Reader 100 times table load speed 100 rows: {0} milliseconds", readerLoadSmallTableTime); writer.WriteLine("10 rows"); writer.WriteLine("Sql Data Adapter 100 times table fill speed 10 rows: {0} milliseconds", AdapterFillTinyTableTime); writer.WriteLine("Sql Data Reader 100 times table load speed 10 rows: {0} milliseconds", readerLoadTinyTableTime); } Process.Start("result_sql_compare.txt"); } private long MeasureExecutionTimeMethod(SqlConnection conn, string query, Action Method) { long time; // know C# // execute single read step outside measurement time, to warm up cache or whatever Method(conn, query); // start timing time = Environment.TickCount; for (int i = 0; i < 100; i++) { Method(conn, query); } // return time in milliseconds return Environment.TickCount - time; } private void ExecuteDataAdapterFillStep(SqlConnection conn, string query) { DataTable tab = new DataTable(); conn.Open(); using (SqlDataAdapter comm = new SqlDataAdapter(query, conn)) { // Adapter fill table function comm.Fill(tab); } conn.Close(); } private void ExecuteSqlReaderLoadStep(SqlConnection conn, string query) { DataTable tab = new DataTable(); conn.Open(); using (SqlCommand comm = new SqlCommand(query, conn)) { using (SqlDataReader reader = comm.ExecuteReader()) { // IDataReader Load function tab.Load(reader); } } conn.Close(); } 

Resultados:

 10000 rows: Sql Data Adapter 100 times table fill speed 10000 rows: 11782 milliseconds Sql Data Reader 100 times table load speed 10000 rows: 26047 milliseconds 1000 rows: Sql Data Adapter 100 times table fill speed 1000 rows: 984 milliseconds Sql Data Reader 100 times table load speed 1000 rows: 2031 milliseconds 100 rows: Sql Data Adapter 100 times table fill speed 100 rows: 125 milliseconds Sql Data Reader 100 times table load speed 100 rows: 235 milliseconds 10 rows: Sql Data Adapter 100 times table fill speed 10 rows: 32 milliseconds Sql Data Reader 100 times table load speed 10 rows: 93 milliseconds 

Para problemas de rendimiento, usar el método SqlDataAdapter.Fill es mucho más eficiente. Entonces, a menos que quieras disparar en el pie, usa eso. Funciona más rápido para conjuntos de datos pequeños y grandes.