简体   繁体   中英

What is the proper way of calling multiple “SQL DataReader”s in C#?

I call ExecuteReader(); to get data, then i need to get another data with another query. My structure's been always like this :

class SomeClass
{
    public static void Main(String[] args)
    {
        SqlConnection sqlConn = new SqlConnection();
        sqlConn.ConnectionString = "some connection string" 

        SqlCommand SQLCmd = new SqlCommand();
        SQLCmd.CommandText = "some query";
        SQLCmd.Connection = sqlConn;
        sqlConn.Open();

        sqlReader = SQLCmd.ExecuteReader();

        while (sqlReader.Read())
        {
            //some stuff here
        }

        sqlReader.Dispose();
        sqlReader.Close();
        sqlConn.Close();

        SQLCmd.CommandText = "another query";
        sqlConn.Open();
        sqlReader = SQLCmd.ExecuteReader();

        while (sqlReader.Read())
        {
            //some other stuff here
        }

        sqlReader.Dispose();
        sqlReader.Close();
        sqlConn.Close();
    }
}

They share the same connection string. What else can they share ? Can they share same sqlConn.Open(); ? What is the proper way of resource allocating and avoiding errors ?

BTW it works as it is. Thanks in advance.

This is how I would write all of that:

class SomeClass
{
 public static void Main(String[] args)
 {
  using (SqlConnection sqlConn = new SqlConnection("some connection string"))
  {
   sqlConn.Open();

   using (SqlCommand comm = new SqlCommand("some query", conn))
   using (var sqlReader = comm.ExecuteReader())
   {
    while (sqlReader.Read())
    {
     //some stuff here
    }
   }

   using (SqlCommand comm = new SqlCommand("some query", conn))
   using (var sqlReader = comm.ExecuteReader())
   {
    while (sqlReader.Read())
    {
     //some other stuff here
    }
   }
  }
 }
}

The using statement handles disposing of items when the block is finished. As for sharing stuff, you could leave the connection open across the commands.

The most important thing to dispose out of all of that would be the connection, but I tend towards honouring a using statement if an item is IDisposable regardless of what it actually does in the background (which is liable to change as it's an implementation detail).

Don't forget, there is also Multiple Active Result Sets (as demonstrated in this answer) from a single command and a single reader, where you advance the reader onto the next result set.


My slightly more flippant answer to how I might write all of that would be:

  return connection.Query<T>(procedureName, param, commandType: CommandType.StoredProcedure); 

Using Dapper ;-)

As alluded to in my comment - if possible, combine the two queries into one and then (if it still produces multiple result sets), use NextResult to move on.

Stealing Adam's structure , but with that change:

class SomeClass
{
 public static void Main(String[] args)
 {
  using (SqlConnection sqlConn = new SqlConnection("some connection string"))
  {
   sqlConn.Open();

   using (SqlCommand comm = new SqlCommand("some query; some other query;", conn))
   using (var sqlReader = comm.ExecuteReader())
   {
    while (sqlReader.Read())
    {
     //some stuff here
    }
    if(sqlReader.NextResult())
    {
      while (sqlReader.Read())
      {
       //some other stuff here
      }
     }
    }
  }
 }
}

The proper way is to wrap SqlConnection s and SqlCommand s in using -statements. This will force Dispose to be invoked on the objects when the using block is left, even if an Exception is thrown. (This is not the case with your current code.)

Something in the line of

using(var cnn = new SqlConnection("connectionstring")){
    cnn.Open();
    using(var cmd = new SqlCommand("SELECT 1")){
       var reader = cmd.ExecuteReader();
       while(reader.Read()) { /* doStuff */ }
    }
}

Regardless of the approach Close / Dispose will not actually close the connection since connection setup is very expensive. It will just return the connection to a connection pool and allow other commands/readers to use it.

To manage resource you can use using like as shown under ...

 SQLCmd.CommandText = "some query";
 SQLCmd.Connection = sqlConn;
 sqlConn.Open();

//using will dispose reader automatically.
 using(sqlReader = SQLCmd.ExecuteReader())
{
   while (sqlReader.Read())
    {
    //some stuff here
    }
}
 //sqlReader.Dispose();
 //sqlReader.Close();
 //sqlConn.Close();

 SQLCmd.CommandText = "another query";
//no need to open connection again.
// sqlConn.Open();
// sqlReader = SQLCmd.ExecuteReader();

 using(sqlReader = SQLCmd.ExecuteReader())
{
   while (sqlReader.Read())
    {
    //some stuff here
    }
}
 //sqlReader.Dispose();
 //sqlReader.Close();
 //sqlConn.Close();

you can use using only for those classes which have implemented IDispose interface. in your example you can use SqlConnection and SqlCommand also with using code block.

Use 'using', you don't need to manually close and dispose.

using (SqlConnection connection = new SqlConnection(connectionString)) 
{    
     connection.Open();
     SqlCommand command = new SqlCommand("spTest", connection);
     command.CommandType = CommandType.StoredProcedure;
     command.Parameters.Add(new SqlParameter("@employeeid", employeeID));
     command.CommandTimeout = 5;

     command.ExecuteNonQuery();
}

Open a new connection every time you need it is a best practices. ADO.net use connection pool to menage connection. http://msdn.microsoft.com/it-it/library/8xx3tyca(v=vs.110).aspx

Dont forget your try catch statements though :)

class SomeClass
{
 public static void Main(String[] args)
 {
  using (SqlConnection sqlConn = new SqlConnection("some connection string"))
  {
   try{
   sqlConn.Open();

   using (SqlCommand comm = new SqlCommand("some query", conn))
   using (var sqlReader = comm.ExecuteReader())
   {
    while (sqlReader.Read())
    {
     //some stuff here
    }
   }

   using (SqlCommand comm = new SqlCommand("some query", conn))
   using (var sqlReader = comm.ExecuteReader())
   {
    while (sqlReader.Read())
    {
     //some other stuff here
    }
   }
   }
   catch()
{
// Do exception catching here or rollbacktransaction if your using begin transact
}
finally
{
sqlConn.Close();
}
  }
 }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM