Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I return multiple result sets with SqlCommand?

See SqlDataReader.NextResult (an SqlDataReader is returned from calling SqlCommand.ExecuteReader):

Advances the data reader to the next result [set], when reading the results of batch Transact-SQL statements.

Example:

string commandText = @"SELECT Id, ContactId
FROM dbo.Subscriptions;

SELECT Id, [Name]
FROM dbo.Contacts;";


List<Subscription> subscriptions = new List<Subscription>();
List<Contact> contacts = new List<Contact>();

using (SqlConnection dbConnection = new SqlConnection(@"Data Source=server;Database=database;Integrated Security=true;"))
{
    dbConnection.Open();
    using (SqlCommand dbCommand = dbConnection.CreateCommand())
    {
        dbCommand.CommandText = commandText;
        using(SqlDataReader reader = dbCommand.ExecuteReader())
        {
            while(reader.Read())
            {
                subscriptions.Add(new Subscription()
                {
                    Id = (int)reader["Id"],
                    ContactId = (int)reader["ContactId"]
                });
            }

            // this advances to the next resultset 
            reader.NextResult();

            while(reader.Read())
            {
                contacts.Add(new Contact()
                {
                    Id = (int)reader["Id"],
                    Name = (string)reader["Name"]
                });
            }
        }
    }
}

Other examples:

  • C# Multiple Result Sets
  • Executing a Query That Returns Multiple Result Sets with SqlDataReader : SqlCommand Select « ADO.Net « C# / CSharp Tutorial

Create a Stored Procedure that has multiple selects, and fill the DataSet.

using (SqlConnection conn = new SqlConnection(connection))
{
    DataSet dataset = new DataSet();
    SqlDataAdapter adapter = new SqlDataAdapter();
    adapter.SelectCommand = new SqlCommand("MyProcedure", conn);
    adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
    adapter.Fill(dataset);
    return dataset;
}

The returned dataset will have a DataTable in it's Tables array for each select in the stored procedure.


Tools like "dapper" make this very easy, regardless of whether you use ad-hoc text queries or stored procedures; for example:

using(var multi = conn.QueryMultiple(sql, args))
{
    var customers = multi.Read<Customer>().AsList(); // first grid
    var regionName = multi.ReadFirstOrDefault<string>(); // second grid
    var addresses = multi.Read<Address>().AsList(); // third grid
    // todo: use those things
}

Individual grids can also be read without buffering (as an open IEnumerable<T> over the reader itself) via the optional parameters to Read[<T>].


I call a sproc and get multiple result sets with object, so you end up with a

List<List<Dictionary<string, object>>>

In the multiResultsSet each results set is then

List<Dictionary<string, object>>

They can be cast to their types and transformed into the models as needed.

After you set up the sproc command with everything needed, pass it to this:

    private static List<List<Dictionary<string, object>>> ProcessReader(SqlCommand command)
    {
        var tables = new List<List<Dictionary<string, object>>>();
        using (var reader = command.ExecuteReader())
        {
            do
            {
                var table = new List<Dictionary<string, object>>();
                while (reader.Read())
                    table.Add(Read(reader));
                tables.Add(table);
            } while (reader.NextResult());
        }
        return tables;
    }

and Read() is fairly straight forward.

    private static Dictionary<string, object> Read(IDataRecord reader)
    {
        var row = new Dictionary<string, object>();
        for (var i = 0; i < reader.FieldCount; i++)
        {
            var val = reader[i];
            row[reader.GetName(i)] = val == DBNull.Value ? null : val;
        }
        return row;
    }

I am using a dictionary approach. You can use Newton Json to convert it to json. This way you are not tied to types and IDataRecord

public List<List<Dictionary<string, object>>> ExecuteSqlReader(string cmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
        {
           var sqlCmd = new SqlCommand(cmd);
           var allRecord = new List<List<Dictionary<string, object>>>();
            using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
            {
                using (var reader = sqlCmd.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        var result = new List<Dictionary<string, object>>();
                        while (reader.Read())
                        {
                            result = GetTableRowData(reader);
                        }

                        allRecord.Add(result);
                    }
                    while (reader.NextResult())
                    {
                        if (reader.HasRows)
                        {
                            var result = new List<Dictionary<string, object>>();
                            while (reader.Read())
                            {
                                result = GetTableRowData(reader);
                            }
                            allRecord.Add(result);
                        }
                    }
                }
            }
            return allRecord;
        }