Popolare la tabella dati dal lettore di dati


103

Sto facendo una cosa di base in C # (MS VS2008) e ho una domanda più sulla corretta progettazione che sul codice specifico.

Sto creando un datatable e quindi provando a caricare il datatable da un datareader (che si basa su una procedura memorizzata SQL). Quello che mi chiedo è se il modo più efficiente per caricare il datatable sia fare un'istruzione while o se esiste un modo migliore.

Per me l'unico inconveniente è che devo digitare manualmente i campi che voglio aggiungere nella mia istruzione while, ma non so nemmeno come automatizzarlo poiché non voglio che tutti i campi dall'SP selezionino solo quelli , ma non è un grosso problema ai miei occhi.

Ho incluso frammenti di codice sotto la totalità di ciò che faccio, anche se per me il codice in sé non è eccezionale e nemmeno quello che sto chiedendo. Oltre a chiedermi della mia metodologia, in seguito chiederò aiuto al codice se la mia strategia è sbagliata / inefficiente.

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

Risposte:


283

È possibile caricare un file DataTabledirettamente da un lettore di dati utilizzando il Load()metodo che accetta un file IDataReader.

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

2
Mi hai salvato la giornata (Y)
Uzair Xlade

1
Questo è quello che ho cercato una settimana!
TheTechy

17

Si prega di controllare il codice sottostante. Automaticamente verrà convertito come 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<DataColumn> listCols = new List<DataColumn>();

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

    }

C'è un'opzione semplice per caricare datareader su datatable, quindi perché qualcuno dovrebbe usarlo?
Abbas

@sarathkumar Buon lavoro .. stavo cercando questo codice
SimpleGuy

@Abbas Coz, il caricamento dei dati integrato è molto lento
SimpleGuy

dt.Load(reader)Inoltre, non sempre funziona: riceverei quei fastidiosi Object reference not set to an instance of an objecterrori, probabilmente quando non ricevo alcuna riga. Qualcosa di manuale come questo torna utile. Ho provato e ha dovuto sbarazzarsi di quelle column.linee nel dtSchema foreachciclo perché si diceva che era un cast illegale boolsu (bool)drow["IsUnique"]. Non ne avevo bisogno, DataTableè sufficiente ottenere i nomi delle colonne per popolare il nuovo . Questo è riuscito ad aiutarmi a superare un ds.Fill(adapter)problema con cui non riuscivo a caricare una tabella di grandi dimensioni SELECT * FROM MyTable.
vapcguy

Un avvertimento: se ci sono valori nulli in una qualsiasi delle colonne, devono essere gestiti o questa funzione causa un'eccezione. Devo controllare if (!dr.IsDBNull(i))come la prossima cosa all'interno di quel forciclo. Poi fai le tue dataRowcose. Ma poi hai bisogno di un elsesu quello, nel caso trovi un null. Se lo fai, devi capire il tipo di colonna che stai aggiungendo e assegnare il null di conseguenza (cioè puoi assegnare String.Emptyse è di tipo System.String, ma devi assegnare 0se è System.Int16(campo booleano) o System.Decimal.
vapcguy

13

Se stai cercando di caricare un DataTable, utilizza SqlDataAdapterinvece:

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

Non è nemmeno necessario definire le colonne. Basta creare il DataTablee Fill.

Ecco la cStringtua stringa di connessione ed sqlè il comando della procedura memorizzata.


1
L'unico problema qui è che se trovi che una colonna / valore causa un'eccezione durante il riempimento, non ti dà alcun dettaglio, come potresti essere in grado di usare a SqlDataReadere leggerli usando un ciclo attraverso i campi.
vapcguy

9

Come ha affermato Sagi nella risposta, DataTable.Load è una buona soluzione. Se stai tentando di caricare più tabelle da un singolo lettore, non è necessario chiamare DataReader.NextResult. Il metodo DataTable.Load fa inoltre avanzare il lettore al set di risultati successivo (se presente).

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

5

Ho esaminato anche questo e dopo aver confrontato il metodo SqlDataAdapter.Fill con le funzioni SqlDataReader.Load, ho scoperto che il metodo SqlDataAdapter.Fill è più del doppio più veloce con i set di risultati che ho utilizzato

Codice utilizzato:

    [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<SqlConnection, string> 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();
    }

risultati:

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

Per i problemi di prestazioni, l'utilizzo del metodo SqlDataAdapter.Fill è molto più efficiente. Quindi, a meno che tu non voglia spararti ai piedi, usalo. Funziona più velocemente per set di dati piccoli e grandi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.