0
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient; // need this for database connection

namespace DatabaseTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void connectButton_Click(object sender, EventArgs e)
        {
         
            string connectionString;
            SqlConnection cnn;

            connectionString = "Server= xxx; Database= nba_database; Integrated Security=True"; // xxx is a placeholder, my connection string is right. Censoring it for privacy 


            cnn = new SqlConnection(connectionString);

            cnn.Open();
            MessageBox.Show("Connection Established!");

            cnn.Close();
        }

        private void displayButton_Click(object sender, EventArgs e)
        {


            string connectionString;
            SqlConnection cnn;

            connectionString = "Server= myServer; Database= nba_database; Integrated Security=True";


            cnn = new SqlConnection(connectionString);

            cnn.Open();
            MessageBox.Show("Connection Established!");

            





           
            // lets query some data from the sql server

            // define variables
            SqlCommand command;
            SqlDataReader dataReader;
            String sql, output = "";

            // define SQL statement!
            sql = "SELECT FirstName, LastName " +
                "FROM Players, Teams " +
                "WHERE Players.TeamID = Teams.TeamID " +
                "AND Teams.Nickname = 'Hawks'";

            String sql2 = "SELECT FirstName, LastName FROM Players WHERE Team = 'Milwaukee Bucks'";

          

            // command statement
            command = new SqlCommand(sql, cnn);
          

            dataReader = command.ExecuteReader();

            // Get table values


            textBox1.Text = command.ExecuteScalar().ToString();


            cnn.Close();
            dataReader.Close();
            command.Dispose();
           
        }

    }
}

I'm trying to connect a C# Visual Studio Application to a SQL Server database and I am getting this error: System.InvalidOperationException: 'There is already an open DataReader associated with this Command which must be closed first.'

When the user clicks the Display button, I want to be able to return the results of a query to a textbox in the c# application.

The error is on this line textBox1.Text = command.ExecuteScalar().ToString();

Juanes30
  • 2,398
  • 2
  • 24
  • 38
Kajin
  • 23
  • 8
  • 2
    You ran `command.ExecuteReader();` on the line right above where the error is. That opens a data reader (as the error says). You then ignore that reader and run `command.ExecuteScalar()` immediately after it. That's your issue. The error is pretty clear - you can't have a data reader open, and then also run another query against the same Command object. It's not really clear what you're trying to do with the code though - you've got a query which returns several fields, but then ExecuteScalar would only return one field. Perhaps you need to return the first column from the datareader? – ADyson Jul 20 '20 at 14:45
  • Does this answer your question? [There is already an open DataReader associated with this Command which must be closed first](https://stackoverflow.com/questions/6062192/there-is-already-an-open-datareader-associated-with-this-command-which-must-be-c) – Ian Kemp Jul 20 '20 at 14:46
  • Why you have `dataReader = command.ExecuteReader(); ` this line of code? If you don't need it then you should remove it. You have reader open in that line and immediately after that you are trying to execute another select command. That's why you are getting this error. – Chetan Jul 20 '20 at 14:47

2 Answers2

3

You must close the datareader before the call to ExecuteScalar(). Try like:

        // command statement
        command = new SqlCommand(sql, cnn);
      

        dataReader = command.ExecuteReader();

        // Get table values
        dataReader.Close(); ///<--close reader 

        textBox1.Text = command.ExecuteScalar().ToString();


        cnn.Close();
        
        command.Dispose();
apomene
  • 14,282
  • 9
  • 46
  • 72
  • that fixed it! Thanks a ton – Kajin Jul 20 '20 at 14:47
  • That fixes the issue technically, but it's also just made dataReader even more redundant than it was before. I think some more nuanced advice about either removing the dataReader, or perhaps learning how to use it, would be more useful. Otherwise this is just a dupe of many other generic questions about the same error message. ExecuteScalar doesn't seem to be the right solution for a query which returns multiple columns and almost certainly multiple rows – ADyson Jul 20 '20 at 14:48
  • @Kajin this might make your error go away, but a) it then makes dataReader so useless you might as well just remove it, and b) it means your code doesn't really make a lot of sense overall - see my comment just above. – ADyson Jul 20 '20 at 14:50
  • @ADyson that makes sense. I removed the dataReader, I must've gotten really confused because I was researching how to do this and one way was to use a data reader and the other said use execute scalar. – Kajin Jul 20 '20 at 15:01
2

Here's the two overlapped operations; a data-reader is active until it is consumed/closed/disposed:

// first active operation
dataReader = command.ExecuteReader();

// second active operation
textBox1.Text = command.ExecuteScalar().ToString();

in reality, you aren't using the first one, so... just remove that line?

More generally: it is a good idea to use using on all ADO.NET objects, and frankly: the ADO.NET API is a mess. Consider using Dapper instead, then all this becomes:

string nickname = "Hawks";
using (var cnn = new SqlConnection(connectionString))
{
    textBox1.Text = cnn.ExecuteScalar<string>(@"
SELECT FirstName, LastName
FROM Players, Teams
WHERE Players.TeamID = Teams.TeamID
AND Teams.Nickname = @nickname", new { nickname }");
}

although note that "scalar" only returns one cell (the first column of the first row); you might want to do something with objects instead:

string nickname = "Hawks";
using (var cnn = new SqlConnection(connectionString))
{
    var players = cnn.Query<Player>(@"
SELECT FirstName, LastName
FROM Players, Teams
WHERE Players.TeamID = Teams.TeamID
AND Teams.Nickname = @nickname", new { nickname }).AsList();
}

where Player looks something like:

public class Player
{
    public string FirstName {get;set;}
    public string LastName {get;set;}
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Or you can use EF, and not worry about raw SQL in your C# code ever again... – Ian Kemp Jul 20 '20 at 14:50
  • 4
    @IanKemp "not writing it myself' wouldn't *remove* my worry over SQL - quite the opposite - it would increase my worry *considerably*. EF is fine in many cases, agreed; but ... saying "not worry about raw SQL" is like telling a web designer not to worry about CSS/LESS, and just let the graphical tool do it for you: nice idea, but rarely a great option if you care about details – Marc Gravell Jul 20 '20 at 14:52
  • @MarcGravell I was actually wondering why I was only getting one result back. Is there any other way to return multiple outputs without using objects? I'm not opposed to objects, I'm still learning and do not understand them quite yet. Bit if objects are the only/best way, I guess I have some learning to do! – Kajin Jul 20 '20 at 15:04
  • @Kajin two ways: one, use a data-reader and learn the voodoo of ADO.NET, or two (and recommended in this case): `var players = conn.Query<(string FirstName, string LastName)>(@"...", ...).AsList()` - this uses a "value tuple", but: it still allows you to, for example: `foreach (var player in players) { AddRow(player.FirstName, player.LastName); }` etc. Just beware: in this scenario, Dapper maps columns by *position* (usually it maps columns by *name*, but that isn't possible here) – Marc Gravell Jul 20 '20 at 15:24
  • @Kajin or I guess option three: use a `DataTable`, but: please don't – Marc Gravell Jul 20 '20 at 15:25