这个命令已经关联了一个打开的DataReader,必须先关闭它。

3

我正在开发一个应用程序,需要连接到另一个数据库获取一些数据,为此,我决定使用SqlConnection、reader等。

我需要执行几个查询,例如,首先我需要为某个用户获取CARD ID,然后我需要通过该CARD ID获取一些数据。

这是我的代码:

#region Connection to another Database

SqlConnection sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;");
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;

cmd.CommandText = "Select * From Users Where CardID=" + "'" + user.CardID + "'";
cmd.CommandType = CommandType.Text;
cmd.Connection = sqlConnection1;

sqlConnection1.Open();

reader = cmd.ExecuteReader();

string cardID = "";
string quantity="";

while (reader.Read())
{
    cardID = reader["CardID"].ToString();
}
//HOW COULD I WRITE ANOTHER QUERY NOW, FOR EXAMPLE, OK I GOT CARDID NOW GIVE ME SOME OTHER THINGS FROM THAT DATABASE BY THAT cardID
//here I tried to change CommandText and to keep working with reader.. but its not working like this because its throwing me exception mention in question title.

cmd.CommandText = "Select T1.CardID, T2.Title, Sum(T1.Quantity) as Quantity From CardTransactions as T1 JOIN Adds as T2 ON T1.AddsID = T2.AddsID Where T1.CardID =" + cardID + "AND T1.Type = 1 Group By T1.CardID, T2.Title";

reader = cmd.ExecuteReader();

while (reader.Read())
{
    quantity = reader["Quantity"].ToString();
}

// Data is accessible through the DataReader object here.

sqlConnection1.Close();

#endregion 

各位,我如何使用这个示例执行几个查询语句。

非常感谢!祝好!


1
你需要关闭并且释放你的命令和读取器。 - Dieter B
1
感谢@Patrick Hofman。我已经将其删除。 - Reyan Chougle
@IanR.O'Brien 这个问题是关于实体的,而这个问题并不涉及实体。 - Roxy'Pro
3个回答

9

你的问题在于没有正确释放正在使用的对象。为此,最好总是使用using结构,因为它将确保所有内容都被释放。请尝试以下代码:

SqlConnection sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;");
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
string cardID = "";
string quantity="";

using(sqlConnection1 = new SqlConnection("Data Source=ComputerOne; Initial Catalog=TestDatabase;Integrated Security=False; User ID=test; Password=test123;"))
{
    sqlConnection1.Open();

    using(cmd = new SqlCommand())
    {
        cmd.CommandText = "Select * From Users Where CardID=" + "'" + user.CardID + "'";
        cmd.CommandType = CommandType.Text;
        cmd.Connection = sqlConnection1;

        using(reader = cmd.ExecuteReader())
        {


            while (reader.Read())
            {
                cardID = reader["CardID"].ToString();
            }
        } //reader gets disposed right here
    } //cmd gets disposed right here

    using(cmd = new SqlCommand())
    {
        cmd.CommandText = "Select T1.CardID, T2.Title, Sum(T1.Quantity) as Quantity From CardTransactions as T1 JOIN Adds as T2 ON T1.AddsID = T2.AddsID Where T1.CardID =" + cardID + "AND T1.Type = 1 Group By T1.CardID, T2.Title";
        cmd.CommandType = CommandType.Text;
        cmd.Connection = sqlConnection1;

        using(reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                quantity = reader["Quantity"].ToString();
            }
        } //reader gets disposed right here
    } //cmd gets disposed right here
    sqlConnection1.Close();
} //sqlConnection1 gets disposed right here

你能解释一下为什么你把所有东西都放在using里面吗?我知道你这样做是为了确保一次只打开一个连接,但其他的东西也需要吗?比如读者等等,我不能只用一个读者完成任务吗?比如:reader.Close(); 然后写新的cmd.CommandText="Select * from Users"; reader=cmd.ExecuteReader(); 等等...关闭读者->写新的cmd文本->将结果添加到读者中...但无论如何感谢你的回答! - Roxy'Pro
@Roxy'Pro 基本上,所做的是在 using 语句中使用 SqlConnection、SqlCommand 和 SqlDataReader。当使用它们时,对象会在块完成后立即被处理。因此,您可以稍后重用它们,我将编辑答案,以便您可以更好地看到它。就像每次使用对象时都调用 Dispose() 一样(这也可以解决此问题,但“using”是正确的方法)。 - NicoRiff
1
@NicoRiff 一个问题是我应该在using语句中放置哪些对象,我的意思是哪种类型的对象应该在块完成后被处理,比如我怎么知道为什么某些东西应该在using语句中。 - user6067265
@NicoRiff 一个问题是我应该在using语句中放置哪些对象,我的意思是哪种类型的对象应该在块完成后被处理,比如我怎么知道为什么某些东西应该在using语句中。 - user6067265

3

您打开的阅读器仍处于活动状态。同时,您一次只能有一个活动的阅读器。为确保正确关闭,请将所有 Sql... 实例包装在 using 中。

using (SqlConnection connection = new SqlConnection(...))
{
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        // the code using reader
    }
}

那么我该如何执行2个或多个查询并使用同一个读取器读取它们呢? - Roxy'Pro
当使用上述代码时,您可以进行操作,但永远不要同时进行两个操作。您需要先关闭第一个操作,然后再继续读取第二个操作。 - Patrick Hofman

-1

嗯...你之所以收到错误是因为第一次调用使用的读取器没有关闭。当您完成使用DataReader对象时,应始终调用Close方法,确保读取器使用的连接返回到连接池(该连接仅由该DataReader独占使用)。部分代码:

reader = cmd.ExecuteReader();
try
{
  while(myReader.Read()) 
  {
    while (reader.Read())
    {
        cardID = reader["CardID"].ToString();
    }
}
finally
{
  myReader.Close();
}
...
reader = cmd.ExecuteReader();
try
{
  while(myReader.Read()) 
  {
        reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            quantity = reader["Quantity"].ToString();
        }
  }
}
finally
{
  myReader.Close();
  myConnection.Close();
}

此外... 作为清晰代码的规则,将您的调用分离到不同的方法中(SOLID原则)。

1
不,建议这样做是危险的。如果发生异常,如果那行代码没有被执行,数据读取器将保持打开状态。 - Patrick Hofman
4
当您使用完DataReader对象后,应始终调用Close方法。如果您的Command包含输出参数或返回值,则这些值在关闭DataReader之前将不可用。请注意,在DataReader打开时,连接专门由该DataReader使用。在原始DataReader关闭之前,您不能执行任何与该连接相关的命令,包括创建另一个DataReader。 - Ionut Ungureanu
另外...在出现异常的情况下,可以充分利用try-catch机制。 - Ionut Ungureanu
请在您的回答中解释这一点。 - Patrick Hofman
@PatrickHofman你的回答比...但Lonut也是正确的。如果问题是“如何保证读者关闭..”,那么你没问题..他正确地解释了错误。 - levent
1
我不同意,只给出可能危险的一半答案并不是解决方案。@levent - Patrick Hofman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接