SqlDataReader的HasRows属性为True但没有数据

7

我正在连接到一个SQL Server 2012数据库,并根据ID查询单个值。(值得一提的是,这个数据库位于另一个大陆的服务器上,因此延迟非常高。大约在100毫秒左右)。

查询似乎成功执行。SqlDataReader对象的HasRows属性被设置为true,因此我尝试使用该值来分配变量。当我正常运行程序时,遇到了一个异常,其消息为“'Given key was not present in the dictionary'”。如果我停止执行并检查SqlDataReader对象,并枚举结果。首先我被告知“'enumeration yielded no results'”,然后当我继续执行时,我会收到一个不同的异常,其消息为“'invalid attempt to read when no data is present'”

以下是相关代码:

SqlConnection sql_conn = new SqlConnection(ConnectionString);
SqlCommand sql_cmd = new SqlCommand(String.Format("select ItemType from ItemTable where ItemID='{0}'", item_id), sql_conn);

Console.WriteLine(sql_cmd.CommandText);

sql_conn.Open();

SqlDataReader rdr = sql_cmd.ExecuteReader();

rdr.Read();

if (rdr.HasRows) //True
{
    item_type= TypesMap[rdr["ItemType"].ToString()]; //Either 'given key not found in dictionary' or 'invalid attempt to read when no data is present'
}

我已经在SQL Server Management Studio中执行了SQL语句,执行成功。我尝试在C#代码中将ItemID硬编码到语句中,但是仍然存在相同的错误。
我该如何进行调试?看起来一切都很正常,直到我尝试访问查询结果时出现问题。

2
不是导致这个问题的但是:我会反转顺序,首先使用HasRows检查是否有任何行,然后使用Read将读取器推进到下一条记录。 - Tim Schmelter
这两个错误信息是由完全不同的情况引起的。你用这段代码收到的是哪一个? - Steve
1
我会跳过 reader.HasRows 并实现以下逻辑: if (reader.Read()) { item_type= TypesMap[rdr["ItemType"].ToString()]; } - Nino
1
如果您只想获取一个值,请使用rdr.ExecuteScalar()。 - Dmitri Trofimov
3个回答

4

您需要进行调试: 看起来从数据库中读取的键在TypesMap不存在

// Wrap IDisposable into using
using (SqlConnection sql_conn = new SqlConnection(ConnectionString)) {
  // Make SQL readable
  // Make SQL parametrized (and not formatted) when it's possible
  String sql = 
    @"select ItemType 
        from ItemTable 
       where ItemID = @prm_ItemId"; 

  // Wrap IDisposable into using
  using (SqlCommand sql_cmd = new SqlCommand(sql, sql_conn)) {
    // I don't know ItemID's type that's why I've put AddWithValue 
    sql_cmd.Parameters.AddWithValue("@prm_ItemId", item_id);

    // Wrap IDisposable into using
    using (SqlDataReader rdr = sql_cmd.ExecuteReader()) {
      // rdr.HasRows is redundant - rdr.Read() returns true if record has been read
      if (rdr.Read()) {
        String key = Convert.ToString(rdr.GetValue(0));
        // Put break point here: what is the "key" value?
        item_type = TypesMap[key];
      }
    }
  } 
}

编辑:正如Luke在评论中提到的,错误的原因是期望键比较不区分大小写,所以修正方法是向.Net解释如何比较键:

var TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

...
TypesMap.Add("aBc", "xyz"); 
String test = TypesMap["Abc"]; // return "xyz"; notice "aBc" and "Abc"

谢谢。最后发现这与数据库查询无关。像往常一样,这只是一个愚蠢的错误,我需要将字典键全部改为小写。 - Luke
1
@Luke:更好的选择是不要将键转换为小写,而是分配适当的比较器TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - Dmitry Bychenko
我之前不知道这个。谢谢! - Luke

2
正如Dmitry所指出的那样,“给定的键未找到...”不是数据库的问题,而是字典的问题。在下面我添加了一个简单的检查来确保该键存在于字典中-如果存在,则可以将其赋值给item_type。
此外,如果HasRows()没有达到您的预期,请尝试以下方法。这是我从数据库读取数据的标准方式:
using (SqlDataReader results = sql_cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
    while (results.Read())
    {
        string Key = rdr["ItemType"].ToString();
        if (TypesMap.ContainsKey(Key))
            item_type = TypesMap[Key];       
    }
}

你的标准做法不是唯一的标准做法。作为标准惯例,你应该始终调用 hasrows。 - Max Alexander Hanna
@MaxAlexanderHanna 嗯,我没有说也没有暗示这是标准做法。只是我个人的做法而已。因此使用了“我”这个词,没有提到“标准做法”。 - Jag
如果它没有行,那么就存在更大的问题,你试图“标准化实践”的尝试实际上是适得其反。我不知道你是如何得到这个答案的两个点的。 - Max Alexander Hanna
@MaxAlexanderHanna 哈哈,真的吗。你拉出一个三年前的答案(甚至不是被采纳的答案),然后指责我是在反生产力? - Jag

1

我转换了:

dto.Id = (int)record["Id"];  

To:

dto.Id = (int)record[0]; 

这对我起作用了。


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