54

I normally use DataSet because It is very flexible. Recently I am assigned code optimization task , To reduce hits to the database I am changing two queries in a procedure. one Query returns the count and the other returns the actual data. That is , My stored procedure returns two tables. Now, I know how to read both tables using DataSets, But I need to read both tables using DataReader. In search of that I found This.

I follow the article and wrote my code like this:

dr = cmd.ExecuteReader();
while (dr.Read())
{


}
if (dr.NextResult()) // this line throws exception
{
   while (dr.Read())
{

But I am getting an exception at dt.NextResult. Exception is :

Invalid attempt to call NextResult when reader is closed.

I also googled above error , but still not able to solve the issue. Any help will be much appreciated. I need to read multiple tables using datareader, is this possible?

muhammad kashif
  • 2,566
  • 3
  • 26
  • 49
  • 1
    What i don't understand is: _" one Query returns the count and the other returns the actual data. That is , My stored procedure returns two tables"_ Why is the count(which is a scalar value) a **table**? – Tim Schmelter Oct 19 '12 at 07:42
  • Yes it is scalar value but the stored procedure is written using dynamic queries . both Queries are very large and Queries are written as varchar and then in the end, executed using Exec. If I call Count query as the sub query of actual data query then the size of query variable is very large and it is Executed with errors. So to avoid this I wrote two different queries this is the reason Count is also coming from a table (second table). I hope I made it clear. – muhammad kashif Oct 19 '12 at 07:48

4 Answers4

68

Try this because this will close connection ,data reader and command once task get over , so that this will not give datareader close exception

Also do check like this if(reader.NextResult()) to check there is next result,

using (SqlConnection connection = new SqlConnection("connection string here"))
{
    using (SqlCommand command = new SqlCommand
           ("SELECT Column1 FROM Table1; SELECT Column2 FROM Table2", connection))
    {
        connection.Open(); 
        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                MessageBox.Show(reader.GetString(0), "Table1.Column1");
            }

            if(reader.NextResult())
            {
               while (reader.Read())
              {
                MessageBox.Show(reader.GetString(0), "Table2.Column2");
              }
            }
        }
    }
}
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
  • @muhammadkashif- your error is cause because either you connection is closed or datareader close so its better to do check for that also like this....also do check for nextresultset in if clause as i did that also helps you.... – Pranay Rana Oct 19 '12 at 07:32
  • 1
    Thanks , I was missing {} after using due to which only first while was considered to be the part of Using statement. Thanks , Will mark your answer as accepted in five minutes. – muhammad kashif Oct 19 '12 at 07:34
  • 1
    @muhammadkashif could you update your question to show how you had the incorrect `using …;` construct? That would clarify the question… – binki Jul 01 '15 at 16:10
  • @Pranay Rana i am return 4 table so how can take the data from 3rd and 4th tables data with your solution can you please help me?? – coderwill May 08 '17 at 08:12
14

I have tried to reproduce this issue (also because i haven't used multiple tables in a reader before). But it works as expected, hence i assume that you've omitted the related code.

Here's my test code:

using (var con = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
    using (var cmd = new SqlCommand("SELECT TOP 10 * FROM tabData; SELECT TOP 10 * FROM tabDataDetail;", con))
    {
        int rowCount = 0;
        con.Open();
        using (IDataReader rdr = cmd.ExecuteReader())
        {
            while (rdr.Read())
            {
                String object1 = String.Format("Object 1 in Row {0}: '{1}'", ++rowCount, rdr[0]);
            }
            if (rdr.NextResult())
            {
                rowCount = 0;
                while (rdr.Read())
                {
                    String object1 = String.Format("Object 1 in Row {0}: '{1}'", ++rowCount, rdr[0]);
                }
            }
        }
    }
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Thanks , My problem is solved , Up voting you for your efforts. – muhammad kashif Oct 19 '12 at 07:42
  • If I want to use DataTable.Load() instead of looping the reader, then second table is getting populated. Any clue on this? – Biki Jun 13 '16 at 15:16
  • 3
    Finally I got the clue. If we are using DataTable.Load() then we need not use rdr.NextResult(), as that is taken care implicitly. – Biki Jun 14 '16 at 07:20
3

I built on Pranay Rana's answer because I like keeping it as small as possible.

string rslt = "";
using (SqlDataReader dr = cmd.ExecuteReader())
{
    do
    {
        while (dr.Read())
        {
            rslt += $"ReqID: {dr["REQ_NR"]}, Shpr: {dr["SHPR_NR"]}, MultiLoc: {dr["MULTI_LOC"]}\r\n";
        }
    } while (dr.NextResult());
}
tolsen64
  • 881
  • 1
  • 9
  • 22
  • This option only works if the schema of the data is the same for each of the returned tables. In the case of the OP's question, this would not work because the schema of the first result is a single record with a single "count" column. And the second result is a full table with multiple columns and records. – jwatts1980 Oct 05 '22 at 22:34
1

The question is old but I find the answers are not correct. Here's how I do it:

        List<DataTable> dataTables = new();
        using IDataReader dataReader = command.ExecuteReader();
        do
        {
            DataTable dataTable = new();
            dataTable.Load(dataReader);
            dataTables.Add(dataTable);
        }
        while (!dataReader.IsClosed);
  • Note that this works because the `DataTable.Load(reader)` method automatically calls "NextResult()" on the reader. `The Load method consumes the first result set from the loaded IDataReader, and after successful completion, sets the reader's position to the next result set, if any.` https://learn.microsoft.com/en-us/dotnet/api/system.data.datatable.load?view=net-6.0 – jwatts1980 Oct 05 '22 at 22:32