Remplir le tableau de données du lecteur de données

Je fais une chose basique en C # (MS VS2008) et j’ai une question plus sur la conception correcte que sur le code spécifique.

Je crée un datatable, puis essaie de charger le datatable à partir d’un datareader (basé sur une procédure stockée SQL). Ce que je me demande, c’est si le moyen le plus efficace de charger la datatable est de faire une déclaration, ou s’il existe une meilleure façon de procéder.

Pour moi, le seul inconvénient est que je dois taper manuellement les champs que je veux append dans mon instruction while, mais je ne sais pas non plus comment automatiser cela car je ne veux pas que tous les champs du SP sélectionnent ceux , mais ce n’est pas une grosse affaire à mes yeux.

J’ai inclus des extraits de code sous la totalité de ce que je fais, bien que pour moi le code lui-même ne soit pas remarquable ou même ce que je demande. En plus de me poser des questions sur ma méthodologie, je demanderai de l’aide pour le code plus tard si ma stratégie est fausse ou inefficace.

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"].ToSsortingng(), dr["country key"].ToSsortingng()); } 

Vous pouvez charger un DataTable directement à partir d’un lecteur de données à l’aide de la méthode Load() qui accepte un IDataReader .

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

Veuillez vérifier le code ci-dessous. Automatiquement il convertira en DataTable

 private void ConvertDataReaderToTableManually() { SqlConnection conn = null; try { ssortingng connSsortingng = ConfigurationManager.ConnectionSsortingngs["NorthwindConn"].ConnectionSsortingng; conn = new SqlConnection(connSsortingng); ssortingng 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) { ssortingng columnName = System.Convert.ToSsortingng(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 vous essayez de charger un DataTable , SqlDataAdapter plutôt SqlDataAdapter place:

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

Vous n’avez même pas besoin de définir les colonnes. Créez simplement le DataTable et Fill le.

Ici, cSsortingng est votre chaîne de connexion et sql est la commande de procédure stockée.

Comme Sagi l’a déclaré dans sa réponse, DataTable.Load est une bonne solution. Si vous essayez de charger plusieurs tables à partir d’un seul lecteur, vous n’avez pas besoin d’appeler DataReader.NextResult. La méthode DataTable.Load avance également le lecteur vers le prochain ensemble de résultats (le cas échéant).

 // 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); } 

Je me suis aussi penché sur cette question et après avoir comparé la méthode SqlDataAdapter.Fill avec les fonctions SqlDataReader.Load, j’ai constaté que la méthode SqlDataAdapter.Fill était deux fois plus rapide avec les jeux de résultats que j’utilisais.

Code utilisé:

  [TestMethod] public void SQLCommandVsAddaptor() { long AdapterFillLargeTableTime, readerLoadLargeTableTime, AdapterFillMediumTableTime, readerLoadMediumTableTime, AdapterFillSmallTableTime, readerLoadSmallTableTime, AdapterFillTinyTableTime, readerLoadTinyTableTime; ssortingng LargeTableToFill = "select top 10000 * from FooBar"; ssortingng MediumTableToFill = "select top 1000 * from FooBar"; ssortingng SmallTableToFill = "select top 100 * from FooBar"; ssortingng 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, ssortingng 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(); } 

Résultats:

 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 

Pour les problèmes de performances, l'utilisation de la méthode SqlDataAdapter.Fill est beaucoup plus efficace. Donc, à moins que vous ne vouliez vous tirer dessus, utilisez-le. Il fonctionne plus rapidement pour les petits et les grands ensembles de données.